JVM直接内存

JVM直接内存

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

正常文件读写流程

直接内存1.png

使用了DirectBuffer(直接内存)

直接内存2.png

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

释放原理

  • 直接内存的回收不是通过JVM的垃圾回收来释放的,而是通过unsafe.freeMemory来手动释放
//通过ByteBuffer申请1M的直接内存
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);
  • 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);
}

直接内存的回收机制总结

  • 使用了Unsafe类来完成直接内存的分配回收,回收需要主动调用freeMemory方法
  • ByteBuffer的实现内部使用了Cleaner(虚引用)来检测ByteBuffer。一旦ByteBuffer被垃圾回收,那么会由ReferenceHandler来调用Cleaner的clean方法调用freeMemory来释放内存
  • (-XX:DisableExplicitGC表示禁止显示的垃圾回收System.gc())如果不用System.gc()方法对ByteBuffer进行回收的话申请的直接内存会长时间存在,知道执行真正的垃圾回收
### JVM直接内存的使用与配置 #### 什么是JVM直接内存JVM直接内存指的是通过`java.nio.ByteBuffer.allocateDirect()`方法分配的位于堆外的内存区域。这种内存并不属于Java堆的一部分,而是由操作系统管理的本地内存[^1]。 #### 直接内存的作用 直接内存主要用于提高I/O操作性能。由于其绕过了Java堆的垃圾回收机制,在处理大文件或者频繁的网络通信时能够显著减少GC压力并提升效率。 #### 如何查看直接内存的使用情况? 可以通过以下几种方式来监控和分析直接内存的使用状况: - 使用 `jcmd` 工具: ```bash jcmd <pid> VM.native_memory summary ``` 这条命令可以帮助我们了解整个JVM进程中不同类型的原生内存(包括直接内存)的具体消耗详情[^2]。 - 利用 `jstat` 或者其他性能监测工具也可以间接观察到非堆内存的变化趋势,从而推测出直接内存的增长态势。 #### 配置直接内存的最大值 虽然理论上直接内存受限于系统的物理可用资源以及 `-Xmx` 参数所定义的最大堆大小之外的空间,但在实际开发过程中为了防止因过度申请而导致OOM (OutOfMemoryError),通常建议显式设置最大允许使用的直接内存量。这可通过调整如下参数实现: ```properties -Dsun.misc.MAX_DIRECT_MEMORY_SIZE=<size> ``` 其中 `<size>` 表示单位字节数量级,默认情况下该属性未被指定,则取值等于元数据区(-XX:MaxMetaspaceSize)/永久代(MaxPermSize)加上年轻代(YoungGen)+老年代(OldGen)[^4]。 如果希望更精确控制每一块细分领域内的行为模式还可以考虑引入额外选项比如启用TLAB优化策略等措施进一步细化粒度级别的调节方案[^3]: ```properties -XX:+UseTLAB -XX:TLABSize=... ``` 以上即是对JVM直接内存基本概念及其常见应用场景下的简单介绍,并提供了若干实用技巧帮助开发者更好地理解和应对可能出现的相关挑战。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值