jvm直接内存

151 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

6.直接内存

(1)定义

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

(2)文件读写流程

未使用DirectBuffer

image-20220331143701259

使用了DirectBuffer

image-20220331143752654

直接内存是操作系统和Java代码都可以访问的一块区域,无需将代码从系统内存复制到Java堆内存,从而提高 了效率

(3)释放原理

直接内存的回收不是通过JVM的垃圾回收来释放的,而是通过unsafe.freeMemory来手动释放

//通过ByteBuffer申请1M的直接内存 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);

申请直接内存,但JVM并不能回收直接内存中的内容,它是如何实现回收的呢?

allocateDirect的实现

public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}
​

DirectByteBuffer类

DirectByteBuffer(int cap) { // package-private
​
     super(-1, 0, cap, cap);
     boolean pa = VM.isDirectMemoryPageAligned();
     int ps = Bits.pageSize();
     long size = Math.max(1L, (long)cap + (pa ? ps : 0));
     Bits.reserveMemory(size, cap);
     long base = 0;
     try {
        base = unsafe.allocateMemory(size); //申请内存
     } catch (OutOfMemoryError x) {
         Bits.unreserveMemory(size, cap);
         throw x;
     }
     unsafe.setMemory(base, size, (byte) 0);
     if (pa && (base % ps != 0)) {
         // Round up to page boundary
         address = base + ps - (base & (ps - 1));
     } else {
        address = base;
     }
     cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); //通过虚引用,来实现直接内存的释放,this为虚引
     att = null;
}

这里调用了一个Cleaner的create方法,且后台线程还会对虚引用的对象监测,如果虚引用的实际对象(这里 是DirectByteBuffer)被回收以后,就会调用Cleaner的clean方法,来清除直接内存中占用的内存

public void clean() {
     if (remove(this)) {
         try {
            this.thunk.run(); //调用run方法
         } catch (final Throwable var2) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
         public Void run() {
             if (System.err != null) {
                (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
             }
             System.exit(1);
             return null;
         }
     });
 }
​

对应对象的run方法

public void run() {     if (address == 0) {         // Paranoia         return;     }     unsafe.freeMemory(address); //释放直接内存中占用的内存     address = 0;     Bits.unreserveMemory(size, capacity); }

(4)分配和回收原理

  • 使用了Unsafe类来完成直接内存的分配回收,回收需要主动调用freeMemory方法
  • ByteBuffer的实现内部使用了Cleaner(虚引用)来检测ByteBuffer。一旦ByteBuffer被垃圾回收,那么 会由ReferenceHandler来调用Cleaner的clean方法调用freeMemory来释放内存