本篇內(nèi)容介紹了“什么是JVM直接內(nèi)存”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比昌邑網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式昌邑網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋昌邑地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。
從上面的圖中可以看到Java8相比Java7來講將方法區(qū)的實現(xiàn),從非堆空間(其實邏輯與堆相連,所屬于運行時數(shù)據(jù)區(qū)內(nèi)部)遷移到了本地內(nèi)存中,不會造成FullGC過多的壓力以及與老年代的耦合度過高的問題,減少FullGC的掃描范圍,從而改為手動去回收機(jī)制(也可以自動回收需要配置調(diào)整)。 之前的文章里面介紹了JVM的運行時數(shù)據(jù)區(qū)的相關(guān)介紹,一直對直接內(nèi)存的研究和學(xué)習(xí)較少,現(xiàn)在我們就開始介紹一下直接內(nèi)存的分配方式以及回收方式。
1. 常見于NIO操作時,用于數(shù)據(jù)緩沖區(qū)(ByteBuffer.allocate),當(dāng)然也可以采用Unsafe類進(jìn)行native方法運作進(jìn)行申請直接內(nèi)存。
2. 分配回收成本較高(屬于操作系統(tǒng)內(nèi)存),但讀寫性能高。(因為直接內(nèi)存不需要經(jīng)過JVM解釋器進(jìn)行地址映射轉(zhuǎn)換到系統(tǒng)真正內(nèi)存,故此讀寫速度會比堆內(nèi)存在快很多,但是申請和回收機(jī)制角度而言復(fù)雜,因為屬于直接由操作系統(tǒng)進(jìn)行管理,而非JVM直接進(jìn)行管理。
3. 不受JVM內(nèi)存回收管理(依舊存在內(nèi)存溢出的問題),此外受限制與操作系統(tǒng)物理內(nèi)存,以及我們可以手動設(shè)置它的閾值(MaxDirectMemory),默認(rèn)情況下來講直接內(nèi)存幾乎沒有限制(當(dāng)沒有超出物理內(nèi)存的控制而言)
4. jvm的內(nèi)存分配與回收是自動的,不需要手動調(diào)用任何的方法,但是直接內(nèi)存需要我們手動調(diào)用方法。
Java堆內(nèi)存:
使用直接內(nèi)存后,可以減少步驟:
可以看出來,直接內(nèi)存無需進(jìn)行與java堆內(nèi)存進(jìn)行映射和轉(zhuǎn)換,可以直接去操作系統(tǒng)內(nèi)存。
書寫程序:每次都分配直接內(nèi)存,直到內(nèi)存溢出
public class Test1 { static int _100Mb=1024*1024*100; public static void main(String[] args) { Listlist=new ArrayList<>(); int i=0; try { while (true){ ByteBuffer byteBuffer=ByteBuffer.allocateDirect(_100Mb); list.add(byteBuffer); i++; } }finally { System.out.println(i); } } }
測試結(jié)果:
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:694) at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311) at pers.zhb.test.Test1.main(Test1.java:15)
運行程序前:
直接內(nèi)存的分配與釋放程序:
public class Test1 { static int _1Gb=1024*1024*1024; public static void main(String[] args) throws IOException { ByteBuffer byteBuffer=ByteBuffer.allocateDirect(_1Gb); System.out.println("分配完畢"); System.in.read(); System.out.println("開始釋放"); byteBuffer=null; System.gc(); } }
分配直接內(nèi)存后:
可以看出來相關(guān)的多出一個java的進(jìn)程,并且內(nèi)存占用1037M,所以也證實了我們的猜測和預(yù)想。
在IDEA的控制臺點擊回車對內(nèi)存進(jìn)行釋放:
可以看到恢復(fù)到了原始的狀態(tài)。
控制臺打印出分配與回收的提示:
分配完畢 開始釋放 Process finished with exit code 0
其中System.gc() ,強(qiáng)制執(zhí)行FullGC,回收掉byteBuffer對象
至此,我們的推測全部驗證通過,直接內(nèi)存會采用另外一個進(jìn)程去存儲相關(guān)的系統(tǒng)內(nèi)存區(qū)域,并且不屬于jvm內(nèi)存之內(nèi)的管理。
public class Test1 { static int _1Gb=1024*1024*1024; public static void main(String[] args) throws IOException { Unsafe unsafe=getUnsafe(); //分配內(nèi)存 long base=unsafe.allocateMemory(_1Gb); unsafe.setMemory(base,_1Gb,(byte)0); System.in.read(); //釋放內(nèi)存 unsafe.freeMemory(base); System.in.read(); } public static Unsafe getUnsafe(){ Field field= null; try { field = Unsafe.class.getDeclaredField("theUnsafe"); } catch (NoSuchFieldException e) { e.printStackTrace(); } field.setAccessible(true); Unsafe unsafe= null; try { unsafe = (Unsafe)field.get(null); } catch (IllegalAccessException e) { e.printStackTrace(); } return unsafe; } }
jvm的內(nèi)存分配與回收是自動的,不需要手動調(diào)用任何的方法,但是直接內(nèi)存需要我們手動調(diào)用方法。
ByteBuffer byteBuffer= ByteBuffer.allocateDirect(_1Gb);
public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); }
DirectByteBuffer(int cap) { super(-1, 0, cap, cap); boolean pa = VM.isDirectMemoryPageAligned(); int ps = Bits.pageSize(); long size = Math.max(1L, (long)cap + (pa ? ps : 0)); Bits.reserveMemory(size, cap); long base = 0; try { base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { Bits.unreserveMemory(size, cap); throw x; } unsafe.setMemory(base, size, (byte) 0); if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); att = null; }
底層用到的依舊是unsafe對象
減少了垃圾回收
堆外內(nèi)存是直接受操作系統(tǒng)管理(不是JVM)。這樣做能保持一個較小的堆內(nèi)內(nèi)存,以減少垃圾收集對應(yīng)用的影響。
提升IO速度
堆內(nèi)內(nèi)存由JVM管理,屬于“用戶態(tài)”;而堆外內(nèi)存由OS管理,屬于“內(nèi)核態(tài)”。如果從堆內(nèi)向磁盤寫數(shù)據(jù)時,數(shù)據(jù)會被先復(fù)制到堆外內(nèi)存,即內(nèi)核緩沖區(qū),然后再由OS寫入磁盤,使用堆外內(nèi)存避免了這個操作。
沒有JVM協(xié)助管理內(nèi)存,需要我們自己手動來管理堆外內(nèi)存,防止內(nèi)存溢出同時也為了避免一直有FULL GC,最終導(dǎo)致物理內(nèi)存被耗完。
我們會指定直接內(nèi)存的最大值,通過-XX:MaxDirectMemorySize來指定,當(dāng)達(dá)到閾值的時候,調(diào)用system.gc來進(jìn)行一次full gc,把那些沒有被使用的直接內(nèi)存回收掉。
此外,分配直接內(nèi)存和回收內(nèi)存的性能和速度也比較復(fù)雜,所以成本也會很高,特別是回收需要FullGC、分配內(nèi)存的時候,需要切換一次用戶態(tài)到系統(tǒng)態(tài)的操作,分配直接內(nèi)存,從而用用戶態(tài)的句柄來引用系統(tǒng)態(tài)的內(nèi)存空間。
“什么是JVM直接內(nèi)存”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!