堆外內(nèi)存的回收機(jī)制

原文:http://www.itdecent.cn/p/35cf0f348275

堆外內(nèi)存

JVM啟動(dòng)時(shí)分配的內(nèi)存,稱(chēng)為堆內(nèi)存,與之相對(duì)的,在代碼中還可以使用堆外內(nèi)存,比如Netty,廣泛使用了堆外內(nèi)存,但是這部分的內(nèi)存并不歸JVM管理,GC算法并不會(huì)對(duì)它們進(jìn)行回收,所以在使用堆外內(nèi)存時(shí),要格外小心,防止內(nèi)存一直得不到釋放,造成線上故障。

堆外內(nèi)存的申請(qǐng)和釋放

JDK的ByteBuffer類(lèi)提供了一個(gè)接口allocateDirect(int capacity)進(jìn)行堆外內(nèi)存的申請(qǐng),底層通過(guò)unsafe.allocateMemory(size)實(shí)現(xiàn),接下去看看在JVM層面是如何實(shí)現(xiàn)的。

image

可以發(fā)現(xiàn),最底層是通過(guò)malloc方法申請(qǐng)的,但是這塊內(nèi)存需要進(jìn)行手動(dòng)釋放,JVM并不會(huì)進(jìn)行回收,幸好Unsafe提供了另一個(gè)接口freeMemory可以對(duì)申請(qǐng)的堆外內(nèi)存進(jìn)行釋放。

image

堆外內(nèi)存的回收機(jī)制

如果每次申請(qǐng)堆外內(nèi)存,都需要在代碼中顯示的釋放,對(duì)于Java這門(mén)語(yǔ)言的設(shè)計(jì)來(lái)說(shuō),顯然不夠合理,既然JVM不會(huì)管理這些堆外內(nèi)存,它們是如何回收的呢?

DirectByteBuffer

JDK中使用DirectByteBuffer對(duì)象來(lái)表示堆外內(nèi)存,每個(gè)DirectByteBuffer對(duì)象在初始化時(shí),都會(huì)創(chuàng)建一個(gè)對(duì)用的Cleaner對(duì)象,這個(gè)Cleaner對(duì)象會(huì)在合適的時(shí)候執(zhí)行unsafe.freeMemory(address),從而回收這塊堆外內(nèi)存。

當(dāng)初始化一塊堆外內(nèi)存時(shí),對(duì)象的引用關(guān)系如下:

image

其中firstCleaner類(lèi)的靜態(tài)變量,Cleaner對(duì)象在初始化時(shí)會(huì)被添加到Clener鏈表中,和first形成引用關(guān)系,ReferenceQueue是用來(lái)保存需要回收的Cleaner對(duì)象。

如果該DirectByteBuffer對(duì)象在一次GC中被回收了

image

此時(shí),只有Cleaner對(duì)象唯一保存了堆外內(nèi)存的數(shù)據(jù)(開(kāi)始地址、大小和容量),在下一次FGC時(shí),把該Cleaner對(duì)象放入到ReferenceQueue中,并觸發(fā)clean方法。

Cleaner對(duì)象的clean方法主要有兩個(gè)作用:
1、把自身從Clener鏈表刪除,從而在下次GC時(shí)能夠被回收
2、釋放堆外內(nèi)存

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

如果JVM一直沒(méi)有執(zhí)行FGC的話(huà),無(wú)效的Cleaner對(duì)象就無(wú)法放入到ReferenceQueue中,從而堆外內(nèi)存也一直得不到釋放,內(nèi)存豈不是會(huì)爆?

其實(shí)在初始化DirectByteBuffer對(duì)象時(shí),如果當(dāng)前堆外內(nèi)存的條件很苛刻時(shí),會(huì)主動(dòng)調(diào)用System.gc()強(qiáng)制執(zhí)行FGC。

image

不過(guò)很多線上環(huán)境的JVM參數(shù)有-XX:+DisableExplicitGC,導(dǎo)致了System.gc()等于一個(gè)空函數(shù),根本不會(huì)觸發(fā)FGC,這一點(diǎn)在使用Netty框架時(shí)需要注意是否會(huì)出問(wèn)題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容