堆外內存

在Java中創建的對象都處於堆內內存(heap)中,堆內內存是由JVM所管控的Java進程內存,並且它們遵循JVM的內存管理機制,JVM會採用垃圾回收機制統一管理堆內存。與之相對的是堆外內存,存在於JVM管控之外的內存區域,Java中對堆外內存的操作,依賴於Unsafe提供的操作堆外內存的native(底層使用c,c++,彙編)方法。

使用堆外內存的原因

  • 對垃圾回收停頓的改善。由於堆外內存是直接受操作系統管理而不是JVM,所以當我們使用堆外內存時,即可保持較小的堆內內存規模。從而在GC時減少回收停頓對於應用的影響。
  • 提升程序I/O操作的性能。通常在I/O通信過程中,會存在堆內內存到堆外內存的數據拷貝操作,對於需要頻繁進行內存間數據拷貝且生命週期較短的暫存數據,都建議存儲到堆外內存。

DirectByteBuffer是Java用於實現堆外內存的一個重要類,通常用在通信過程中做緩衝池,如在Netty、MINA等NIO框架中應用廣泛。DirectByteBuffer對於堆外內存的創建、使用、銷燬等邏輯均由Unsafe提供的堆外內存API來實現。

 

那麼如何通過構建垃圾回收追蹤對象Cleaner實現堆外內存釋放呢?

Cleaner繼承自Java四大引用類型之一的虛引用PhantomReference(衆所周知,無法通過虛引用獲取與之關聯的對象實例,且當對象僅被虛引用引用時,在任何發生GC的時候,其均可被回收),通常PhantomReference與引用隊列ReferenceQueue結合使用,可以實現虛引用關聯對象被垃圾回收時能夠進行系統通知、資源清理等功能。如下圖所示,當某個被Cleaner引用的對象將被回收時,JVM垃圾收集器會將此對象的引用放入到對象引用中的pending鏈表中,等待Reference-Handler進行相關處理。其中,Reference-Handler爲一個擁有最高優先級的守護線程,會循環不斷的處理pending鏈表中的對象引用,執行Cleaner的clean方法進行相關清理工作。

所以當DirectByteBuffer僅被Cleaner引用(即爲虛引用)時,其可以在任意GC時段被回收。當DirectByteBuffer實例對象被回收時,在Reference-Handler線程操作中,會調用Cleaner的clean方法根據創建Cleaner時傳入的Deallocator來進行堆外內存的釋放。 

轉載自:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章