原文
https://www.cnblogs.com/moonandstar08/p/5107648.html
簡介
堆內(nèi)存完全由JVM負(fù)責(zé)分配和釋放,如果程序沒有缺陷代碼導(dǎo)致內(nèi)存泄露,那么就不會(huì)遇到j(luò)ava.lang.OutOfMemoryError這個(gè)錯(cuò)誤琳钉。
使用堆外內(nèi)存瓦糕,就是為了能直接分配和釋放內(nèi)存,提高效率苍息。JDK5.0之后,代碼中能直接操作本地內(nèi)存的方式有2種:使用未公開的Unsafe和NIO包下ByteBuffer。
關(guān)于Unsafe對象的簡介和獲取方式,可以參考:http://blog.csdn.net/aitangyong/article/details/38276681
使用ByteBuffer分配本地內(nèi)存則非常簡單屈藐,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)即可。
ByteBuffer使用
package com.mergades.nio;
import java.nio.ByteBuffer;
public class TestByteBuffer {
//運(yùn)行參數(shù) -verbose:gc -XX:+PrintGCDetails -XX:DisableExplicitGC -XX:MaxDirectMemorySize=10M
public static void main(String[] args) {
while (true) {
ByteBuffer bb = ByteBuffer.allocateDirect(10 * 1024 * 1024);
}
}
}
增加了-XX:+DisableExplicitGC熙尉,這個(gè)參數(shù)作用是禁止代碼中顯示調(diào)用GC联逻。代碼如何顯示調(diào)用GC呢,通過System.gc()函數(shù)調(diào)用检痰。如果加上了這個(gè)JVM啟動(dòng)參數(shù)包归,那么代碼中調(diào)用System.gc()沒有任何效果,相當(dāng)于是沒有這行代碼一樣铅歼。
顯然堆內(nèi)存(包括新生代和老年代)內(nèi)存很充足公壤,但是堆外內(nèi)存溢出了。也就是說NIO直接內(nèi)存的回收椎椰,需要依賴于System.gc()厦幅。如果我們的應(yīng)用中使用了java nio中的direct memory,那么使用-XX:+DisableExplicitGC一定要小心慨飘,存在潛在的內(nèi)存泄露風(fēng)險(xiǎn)慨削。
我們知道java代碼無法強(qiáng)制JVM何時(shí)進(jìn)行垃圾回收,也就是說垃圾回收這個(gè)動(dòng)作的觸發(fā)套媚,完全由JVM自己控制缚态,它會(huì)挑選合適的時(shí)機(jī)回收堆內(nèi)存中的無用java對象。代碼中顯示調(diào)用System.gc()堤瘤,只是建議JVM進(jìn)行垃圾回收玫芦,但是到底會(huì)不會(huì)執(zhí)行垃圾回收是不確定的,可能會(huì)進(jìn)行垃圾回收本辐,也可能不會(huì)桥帆。什么時(shí)候才是合適的時(shí)機(jī)呢医增?一般來說是,系統(tǒng)比較空閑的時(shí)候(比如JVM中活動(dòng)的線程很少的時(shí)候)老虫,還有就是內(nèi)存不足叶骨,不得不進(jìn)行垃圾回收。我們例子中的根本矛盾在于:堆內(nèi)存由JVM自己管理祈匙,堆外內(nèi)存必須要由我們自己釋放忽刽;堆內(nèi)存的消耗速度遠(yuǎn)遠(yuǎn)小于堆外內(nèi)存的消耗,但要命的是必須先釋放堆內(nèi)存中的對象夺欲,才能釋放堆外內(nèi)存跪帝,但是我們又不能強(qiáng)制JVM釋放堆內(nèi)存。
Direct Memory的回收機(jī)制:Direct Memory是受GC控制的些阅,例如ByteBuffer bb = ByteBuffer.allocateDirect(1024)伞剑,這段代碼的執(zhí)行會(huì)在堆外占用1k的內(nèi)存,Java堆內(nèi)只會(huì)占用一個(gè)對象的指針引用的大小市埋,堆外的這1k的空間只有當(dāng)bb對象被回收時(shí)黎泣,才會(huì)被回收,這里會(huì)發(fā)現(xiàn)一個(gè)明顯的不對稱現(xiàn)象缤谎,就是堆外可能占用了很多聘裁,而堆內(nèi)沒占用多少,導(dǎo)致還沒觸發(fā)GC弓千,那就很容易出現(xiàn)Direct Memory造成物理內(nèi)存耗光。
Direct ByteBuffer分配出去的內(nèi)存其實(shí)也是由GC負(fù)責(zé)回收的献起,而不像Unsafe是完全自行管理的洋访,Hotspot在GC時(shí)會(huì)掃描Direct ByteBuffer對象是否有引用,如沒有則同時(shí)也會(huì)回收其占用的堆外內(nèi)存谴餐。
參考:
http://blog.csdn.net/aitangyong/article/details/39403031
http://blog.csdn.net/aitangyong/article/category/2159887
http://blog.csdn.net/aitangyong/article/details/39323125
http://hellojava.info/?p=56
http://hellojava.info/?tag=maxdirectmemorysize
https://yq.aliyun.com/articles/2948
使用堆外內(nèi)存與對象池都能減少GC的暫停時(shí)間姻政,這是它們唯一的共同點(diǎn)。生命周期短的可變對象岂嗓,創(chuàng)建開銷大汁展,或者生命周期雖長但存在冗余的可變對象都比較適合使用對象池。生命周期適中厌殉,或者復(fù)雜的對象則比較適合由GC來進(jìn)行處理食绿。然而,中長生命周期的可變對象就比較棘手了公罕,堆外內(nèi)存則正是它們的菜器紧。
堆外內(nèi)存的好處是:
(1)可以擴(kuò)展至更大的內(nèi)存空間。比如超過1TB甚至比主存還大的空間;
(2)理論上能減少GC暫停時(shí)間;
(3)可以在進(jìn)程間共享楼眷,減少JVM間的對象復(fù)制铲汪,使得JVM的分割部署更容易實(shí)現(xiàn);
(4)它的持久化存儲(chǔ)可以支持快速重啟熊尉,同時(shí)還能夠在測試環(huán)境中重現(xiàn)生產(chǎn)數(shù)據(jù)
站在系統(tǒng)設(shè)計(jì)的角度來看,使用堆外內(nèi)存可以為你的設(shè)計(jì)提供更多可能掌腰。最重要的提升并不在于性能狰住,而是決定性的
堆外內(nèi)存說明請參見:http://ju.outofmemory.cn/entry/200574