CMS GC會不會回收Direct ByteBuffer的內存

微信上有同窗(也是同事)發消息問我這個問題,回答是:Oracle JDK 6u32前的版本不會。java

Direct ByteBuffer是在Java Heap外分配內存,NIO等東西里使用的比較多,但Direct ByteBuffer分配出去的內存其實也是由GC負責回收的,而不像以前一篇文章裏的Unsafe是徹底自行管理的,Hotspot在GC時會掃描Direct ByteBuffer對象是否有引用,如沒有則同時也會回收其佔用的堆外內存,但不幸的是在6u32前的版本里,CMS GC有bug會致使可能回收不掉,具體的bug id爲7112034,在連接的Backport信息裏,能夠看到這個bug是在hotspot 20.7的版本里修復的(hotspot的版本號經過java -version的最後一行Java Hotspot Version之類的能夠看到),6u32帶的就是這個版本,因此6u32是會回收的。微信

回收不掉的狀況下會形成的問題是明明已經不用了,但堆外內存仍然被消耗掉,悲慘的狀況下可能會致使堆外內存耗光。對象

Direct ByteBuffer除了上面這個bug可能形成堆外內存耗光外,還有一種場景也可能會形成堆外內存耗光,如Direct ByteBuffer對象晉升到了Old區,那這個時候就只能等Full GC觸發(CMS GC的狀況下等CMS GC),所以在Direct ByteBuffer使用較多,存活時間較長的狀況下,有可能會致使堆外內存耗光(由於Direct ByteBuffer自己對象所佔用的空間是很小的)。進程

對於上面這種類型的應用,最好是在啓動參數上增長-XX:MaxDirectMemorySize=x[m|g],例如-XX:MaxDirectMemorySize=500m內存

這個參數默認的大小是-Xmx的值(在沒設置MaxDirectMemorySize參數的狀況下,用jinfo -flag等方式會看到默認值是-1,但VM.maxDirectMemory這個方法裏發現是-1,則會以-Xmx做爲默認值),此參數的含義是當Direct ByteBuffer分配的堆外內存到達指定大小後,即觸發Full GC(這段邏輯請見Bits.reserveMemory的代碼),如Full GC後仍然分配不出Direct ByteBuffer須要的空間,則會報OOM錯誤:
java.lang.OutOfMemoryError: Direct buffer memoryget

由於上面所說的情況,如碰到堆外內存佔用較多的場景,能夠嘗試強制執行Full GC(強制的方法爲執行jmap -histo:live)看看,多執行一兩次,如堆外內存降低的話,頗有可能就是Direct ByteBuffer形成的,對於這種狀況,一般加上上面的啓動參數就可解決。it

不少狀況下,咱們會看到Java進程佔用的內存超過-Xmx的大小,緣由就是相似Direct ByteBuffer、Unsafe、GC、編譯、本身寫的JNI模塊等這些是須要佔用堆外空間的。io

相關文章
相關標籤/搜索