直接内存

本文探讨了Java中的直接内存(Direct Memory),它不属于JVM内存,常用于NIO操作以提高数据缓冲区的读写性能。通过文件拷贝的直接内存与普通IO对比实验,展示了直接内存的性能优势。同时,文章也指出直接内存的分配和释放成本较高,且需手动管理,不当使用可能导致内存溢出。最后,分析了直接内存的分配和释放原理,包括Unsafe对象的使用以及ByteBuffer的Cleaner机制。
摘要由CSDN通过智能技术生成

定义

Direct Memory,不属于jvm内存,属于操作系统

  • 常见于NIO操作,用于数据缓冲区
  • 分配回收成本高,但读写性能高
  • 直接内存不受JVM内存回收管理

文件拷贝测试直接内存速度

public class Main{
    static final String FROM = "C:\\Users\\lenovo\\Videos\\Captures\\uni-zuaflea_pages_index_index.vue - HBuilder X 3.2.16 2022-01-14 15-11-00.mp4";
    static final String TO = "D:\\a.mp4";
    static final int _1M = 1024*1024;

    public static void main(String[] args){
        directBuffer();
        io();
    }

    private static void directBuffer(){
        long start = System.nanoTime();
        try(
                FileChannel from = new FileInputStream(FROM).getChannel();
                FileChannel to = new FileOutputStream(TO).getChannel();
        ){
            ByteBuffer buffer = ByteBuffer.allocateDirect(_1M);
            while(true){
                int len = from.read(buffer);
                if(len==-1){
                    break;
                }
                buffer.flip();
                to.write(buffer);
                buffer.clear();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("直接内存用时:"+(end-start)/1000);
    }


    private static void io(){
        long start = System.nanoTime();
        try(
                FileInputStream from = new FileInputStream(FROM);
                FileOutputStream to = new FileOutputStream(TO);
        ){
            byte[] buffer = new byte[_1M];
            while(true){
                int len = from.read(buffer);
                if(len==-1){
                    break;
                }
                to.write(buffer,0,len);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

        long end = System.nanoTime();
        System.out.println("普通io用时:"+(end-start)/1000);
    }
}

在这里插入图片描述

磁盘读写原理

一般IO

在这里插入图片描述

NIO

在这里插入图片描述

使用直接内存之后,Java和操作系统都可以访问这一块区域,相当于将两个缓冲区合并了,IO性能提升

内存溢出

public class Main{
    static int _100M = 100 * 1024 * 1024;

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i=1;
        try{
            while(true){
                i++;
                list.add(ByteBuffer.allocateDirect(_100M));
            }
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            System.out.println(i);
        }
    }
}

在这里插入图片描述

内存分配和释放原理

Java对直接内存的分配和释放,使用的是Unsafe对象

Unsafe申请内存案例

public class Main{
    static final int _1GB = 1024 * 1024 * 1024;

    public static void main(String[] args) {
        Unsafe unsafe = getUnsafe();
        // 申请直接内存,base是申请到的直接内存的地址
        long base = unsafe.allocateMemory(_1GB);
        // 设置直接内存
        unsafe.setMemory(base,_1GB,(byte) 0);

        // 释放base指向的直接内存
        unsafe.freeMemory(base);
    }

    /**
     * Unsafe类仅仅有一个private的构造器,不希望开发者使用Unsafe,可以通过反射来获取类内部的私有Unsafe对象
     * 通过反射获取Unsafe对象
     * @return
     */
    private static Unsafe getUnsafe(){
        try{
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
            return unsafe;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

源码分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分配和回收原理总结

  • 使用了Unsafe对象完成直接内存的分配和回收,并且回收需要主动调用freeMemory方法
  • ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值