Java堆外内存的回收机制

作者 : 开心源码 本文共1184个字,预计阅读时间需要3分钟 发布时间: 2022-05-12 共139人阅读

1 堆外内存

JVM启动时分配的内存,称为堆内存,与之相对的,在代码中还可以使用堆外内存,不如Netty,广泛使用了堆外内存,但是这部分内存不归JVM管理,GC算法并不会对它们进行回收,所以使用堆外内存是需要格外小心,以防出现内存泄露。

2 堆外内存的申请和释放

JDK中使用DirectByteBuffer对象来表示堆外内存,可以通过-XX:MaxDirectMemorySize来指定最大的堆外内存,每个DirectByteBuffer对象在初始化时,都会创立一个对应的Cleaner对象,在Cleaner对象回收的时候回收这部分堆外内存。初始化时引用关系如下:

image

其中first是Cleaner类的静态变量,Cleaner对象在初始化时会被增加到Clener链表中,和first形成引用关系,ReferenceQueue是用来保存需要回收的Cleaner对象。

3 Cleaner如何与GC相关联

JDK除了StrongReference、SoftReference和WeakReference之外,还有一种PhantomReference是虚引用,Cleaner就是PhantomReference的子类。(针对这几种引用,后续专题讲解)

当GC时发现它除了PhantomReference外已不可达(持有它的DirectByteBuffer失效了),就会把它放进 Reference类pending list静态变量里。而后另有一条ReferenceHandler线程,名字叫 “Reference Handler”的,关注着这个pending list,假如看到有对象类型是Cleaner,就会执行它的clean(),在最终的解决里会通过Unsafe的free接口来释放DirectByteBuffer对应的堆外内存块。

4 堆外内存基于GC的回收

快速回顾一下堆内的GC机制,当新生代满了,就会发生young gc;假如此时对象还没失效,就不会被回收;撑过几次young gc后,对象被迁移到老生代;当老生代也满了,就会发生full gc。

这里可以看到一种尴尬的情况,由于DirectByteBuffer本身的个头很小,只需熬过了young gc,即便已经失效了也能在老生代里舒服的呆着,不容易把老生代撑爆触发full gc,假如没有别的大块头进入老生代触发full gc,就一直在那耗着,占着一大片堆外内存不释放。

    其实在初始化DirectByteBuffer对象时,假如当前堆外内存的条件很苛刻时,会主动调用System.gc()强制执行FGC。

这时,就只能靠触发system.gc()来救场了。假如还是无法释放,即可能会出现OOM。

  不过很多线上环境的JVM参数有-XX:+DisableExplicitGC,导致了System.gc()等于一个空函数,根本不会触发FGC,这点需要特别关注。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java堆外内存的回收机制

发表回复