首页 > 系统相关 >堆外内存的回收机制分析

堆外内存的回收机制分析

时间:2022-08-17 17:35:21浏览次数:83  
标签:Cleaner 堆外 对象 回收 内存 JVM

堆外内存

JVM启动时分配的内存,称为堆内存,与之相对的,在代码中还可以使用堆外内存,比如Netty,广泛使用了堆外内存,但是这部分的内存并不归JVM管理,GC算法并不会对它们进行回收,所以在使用堆外内存时,要格外小心,防止内存一直得不到释放,造成线上故障。

堆外内存的申请和释放

JDK的ByteBuffer类提供了一个接口allocateDirect(int capacity)进行堆外内存的申请,底层通过unsafe.allocateMemory(size)实现,接下去看看在JVM层面是如何实现的。

   

可以发现,最底层是通过malloc方法申请的,但是这块内存需要进行手动释放,JVM并不会进行回收,幸好Unsafe提供了另一个接口freeMemory可以对申请的堆外内存进行释放。

   

堆外内存的回收机制

如果每次申请堆外内存,都需要在代码中显示的释放,对于Java这门语言的设计来说,显然不够合理,既然JVM不会管理这些堆外内存,它们是如何回收的呢?

DirectByteBuffer

JDK中使用DirectByteBuffer对象来表示堆外内存,每个DirectByteBuffer对象在初始化时,都会创建一个对用的Cleaner对象,这个Cleaner对象会在合适的时候执行unsafe.freeMemory(address),从而回收这块堆外内存。

当初始化一块堆外内存时,对象的引用关系如下:

   

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

如果该DirectByteBuffer对象在一次GC中被回收了

   

此时,只有Cleaner对象唯一保存了堆外内存的数据(开始地址、大小和容量),在下一次FGC时,把该Cleaner对象放入到ReferenceQueue中,并触发clean方法。

Cleaner对象的clean方法主要有两个作用:
1、把自身从Clener链表删除,从而在下次GC时能够被回收
2、释放堆外内存

public void run() {
    if (address == 0) {
        // Paranoia
        return;
    }
    unsafe.freeMemory(address);
    address = 0;
    Bits.unreserveMemory(size, capacity);
}

如果JVM一直没有执行FGC的话,无效的Cleaner对象就无法放入到ReferenceQueue中,从而堆外内存也一直得不到释放,内存岂不是会爆?

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

   

不过很多线上环境的JVM参数有-XX:+DisableExplicitGC,导致了System.gc()等于一个空函数,根本不会触发FGC,这一点在使用Netty框架时需要注意是否会出问题。



二、直接内存回收流程总结

  1. 业务代码 将 DirectByteBuffer置为null,表示想要回收这块指向的堆外内存
  2. JVM垃圾回收器检测到该DirectByteBuffer对象不可达,将其回收,然后将它对应的虚引用对象Cleaner放到Reference的pending属性中
  3. 后台守护线程ReferenceHandler执行tryHandlePending()方法。检测到pending属性不为空,则拿到Cleaner对象,然后调用Cleaner对象的clean方法
  4. 在Cleaner对象的clean()方法中,会调用DirectByteBuffer的内部类Deallocator的run()方法。在run方法中,会调用unsafe.freeMemory()方法,从而释放了堆外内存。

标签:Cleaner,堆外,对象,回收,内存,JVM
From: https://www.cnblogs.com/henuliulei/p/16596013.html

相关文章

  • k8s 集群节点 SLUB: Unable to allocate memory on node -1 cgroup 内存泄露问题
    1.原因cgroup的kmemaccount特性在3.x内核上有内存泄露问题,如果开启了kmemaccount特性会导致可分配内存越来越少,直到无法创建新pod或节点异常几点解释:kmema......
  • 视频融合云服务EasyCVR平台如何切换为内存模式?
    EasyCVR平台具备较强的视频能力,可支持海量设备接入、视频监控、视频录像、云存储、回放与检索、智能告警、平台级联等功能。平台可将分散在各处的视频监控资源进行统一接入......
  • iis占用服务器内存,W3wp.exe 进程占用内存高消耗CPU近100%导致网站反应速度缓慢的解决
    iis占用服务器内存,W3wp.exe进程占用内存高消耗CPU近100%导致网站反应速度缓慢的解决方案如何降低W3WP.EXE占用的内存和CPU?结合网上的诸多建议,主要的解决办法是:a.在I......
  • String在内存中如何分布
    一、设计思想及原理设计思想1、字符串分配和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。2、JVM......
  • Cache写策略:Cache和内存的友好沟通
    出现背景本质上是上级存储器对下级存储器的写操作,没别的 算法当Cache命中时(要写的块正好在Cache里)1、全写法Cache来全写了,CPU在尽力写了,内存在干啥?#内存状态就是说......
  • linux 命令查看内存和cpu信息
    linux命令查看内存和cpu信息1、查看CPU信息命令cat/proc/cpuinfo2、使用top命令查看CPU的使用情况top3、free命令查看内存free4、df(diskfree):显示磁盘分区上......
  • 内存管理
    内存管理不同语言的内存管理不同语言的内存管理方式:C/C++这种内存堆空间的申请和释放完全靠自己管理Java依赖JVM来做内存管理,不了解jvm内存管理的机制,很可能会因......
  • PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏
    一:背景前几篇我们聊的都是非托管内存泄漏,这一篇我们再看下如何用PerfView来排查托管内存泄漏,其实托管内存泄漏比较好排查,尤其是用WinDbg,毕竟C#是带有丰富的元数据......
  • JVM查看内存使用状况
    1、jps:查看本地正在运行的java进程和进程ID(pid)2、jinfopid,查看指定pid的所有JVM信息1)jinfo-flagspid查询虚拟机运行参数信息。2)jinfo-flagnamepid,查询......
  • C++ Linux下使用共享内存
    在Linux下可以使用SystemV共享内存段实现共享内存,一共有4个API:创建共享内存段或使用已经创建的共享内存段-shmget()将进程附加到已经创建的共享内存段-shmat()从已......