JVM——15.定位 堆外内存 OOM

本文详细探讨了Java中的堆外内存(DirectByteBuffer)及其与JVM的关系。分析了堆外内存的申请、释放过程,通过源码解释了如何通过 Cleaner 和虚引用实现内存回收。文章通过三个模拟实验展示了堆外内存OOM的发生条件,并给出了定位和解决堆外内存溢出问题的策略,强调了合理配置JVM参数的重要性。
摘要由CSDN通过智能技术生成

1. ByteBuffer 堆外内存介绍

在介绍OOM那篇文章中,对堆外内存进行了介绍,就直接把它复制过来;

ByteBuffer和DirectByteBuffer:

  • ByteBuffer:字节缓冲区,它有两种实现:
    • HeapByteBuffer:使用jvm堆内存的字节缓冲区;(对应 ByteBuffer源码中的 allocate()方法)
    • DirectByteBuffer:使用堆外内存,不受jvm堆大小限制;(对应 ByteBuffer源码中的allocateDirect()方法)
  • DirectByteBuffer:ByteBuffer对于使用堆外内存的实现,堆外内存直接使用unsafe方法请求堆外内存空间,读写数据;
    • 源码:
      public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer> {
             
          // ... 省略
          public static ByteBuffer allocateDirect(int capacity) {
             
              return new DirectByteBuffer(capacity);
          }
          // ... 省略
      }
      

DirectByteBuffer与堆外内存的关系:

  • 我们使用allocateDirect()方法生成的DirectByteBuffer对象,本身是存储在jvm堆中的,但是会在堆外内存中划分一块内存区域与这个对象关联起来;
  • 在YGC或者FGC 回收 DirectByteBuffer对象的时候,会通过虚引用(对虚引用不了解的可以查看前面的文章)来释放它关联的堆外内存空间(由Cleaner类实现);
  • Java NIO 在每次分配堆外内存会进行判断,如果堆外内存空间不足时,使用 System.gc() 尝试释放内存,再次进行判断;

堆外内存空间大小设置:

  • jvm参数指定堆外内存大小:-XX:MaxDirectMemorySize=512m;
  • 但是如果没有手动指定时,用我们前面介绍的获取jvm参数的默认值命令:java -XX:+PrintFlagsFinal -version | grep MaxDirectMemorySize获取到的大小为0;
    其实它这里是使用了VM类中代码来指定的大小:
    public class VM {
         
        private static long directMemory = 64 * 1024 * 1024;
        public static long maxDirectMemory() {
         
            return directMemory;
        }
    }
    
    也就是说堆外内存默认大小为64M;如果指定了jvm参数,就使用指定的大小值;

2. ByteBuffer 堆外内存申请、释放(源码分析)

2.1 堆外内存申请

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1 * 1024 * 1024);
public static ByteBuffer allocateDirect(int capacity) {
   
    return new DirectByteBuffer(capacity);
}

在使用ByteBuffer的静态方法allocateDirect()申请内存时,会使用 DirectByteBuffer类的构造方法创建一个DirectByteBuffer对象:

DirectByteBuffer(int cap) {
   
    super(-1, 0, cap, cap);
    boolean pa = VM.isDirectMemoryPageAligned();
    int ps = Bits.pageSize();
    long size = Math.max(1L, (long)cap + (pa ? ps : 0));
    // 判断是否有足够的空间可供申请
    // size:根据是否按页对齐,得到的真实需要申请的内存大小
    // cap:用户指定需要的内存大小(<=size)
    Bits.reserveMemory(size, cap);
    long base = 0;
    try {
   
        // 调用 UNsafe方法申请内存
        base = unsafe.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值