學(xué)習(xí)java的同學(xué)注意了!?。?nbsp;學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號碼:183993990 我們一起學(xué)Java!
程序計(jì)數(shù)器
用于給字節(jié)碼解釋器來選取嚇一跳需要執(zhí)行的字節(jié)碼指令。每個(gè)線程有一個(gè)獨(dú)立的程序計(jì)數(shù)器去,且各個(gè)線程之間互不影響。如果線程正在執(zhí)行一個(gè)Java方法,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的內(nèi)存地址;如果執(zhí)行的是Native方法。在計(jì)數(shù)器為Undefined。此區(qū)域是JVM規(guī)范中唯一一個(gè)不存在OOM的區(qū)域
虛擬機(jī)棧(局部變量空間)
存放編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象應(yīng)用(reference)。64位的double、long占用2個(gè)槽。內(nèi)存空間在編譯期間就能確定,當(dāng)進(jìn)入一個(gè)方式時(shí),這個(gè)方法需要分配的局部變量空間是完全確定的,通過-Xss設(shè)置內(nèi)存容量
異常狀況:
StackOverflowError 棧深度大于虛擬機(jī)所允許的深度
OOM 如果虛擬機(jī)??梢詣討B(tài)擴(kuò)展(當(dāng)前大部分Java虛擬機(jī)都可以動態(tài)擴(kuò)展,只不過Java虛擬機(jī)規(guī)范中的也允許固定長度的虛擬機(jī)棧),如果擴(kuò)展是無法申請到足夠的內(nèi)存
在單個(gè)線程下,無論是猶豫棧幀太大還是虛擬機(jī)棧容量太小,當(dāng)內(nèi)存無法分配的時(shí)候,虛擬機(jī)拋出的都是StackOverflowError
/*** VM Args:-Xss128k* * stack length:2402 Exception in thread "main" java.lang.StackOverflowError*/public class JavaVMStackSOF {PRivate int stackLength = 1;public void stackLeak() { stackLength++; stackLeak();}public static void main(String[] args) throws Throwable { JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch (Throwable e) { System.out.println("stack length:" + oom.stackLength); throw e; }}}如果測試時(shí)不限于單線程,通過不斷的建立線程的方式到是可以產(chǎn)生內(nèi)存溢出的異常。但是這樣產(chǎn)生的內(nèi)存溢出異常與占空間是否足夠大并不存在任何聯(lián)系,或者準(zhǔn)確的說,在這種情況下,為每個(gè)線程的棧分配的內(nèi)存越大,反而越容易產(chǎn)生內(nèi)存溢出的異常。
因?yàn)?a href="http://www.companysz.com/system.asp">操作系統(tǒng)分配給每個(gè)進(jìn)程的內(nèi)存是有限制的,譬如32的window限制為2GB。此測試是通過創(chuàng)建大量的線程。每個(gè)線程占用棧內(nèi)存分配大量的內(nèi)存,導(dǎo)致系統(tǒng)沒有足夠的內(nèi)存。使其不能自動擴(kuò)展
/** * VM Args:-Xss2M (這時(shí)候不妨設(shè)大些) * * java.lang.OutOfMemoryError:unable to create new native thread */public class JavaVMStackOOM { private void dontStop() { while (true) { } } public void stackLeakByThread() { while (true) { Thread thread = new Thread(new Runnable() { @Override public void run() { dontStop(); } }); thread.start(); } } public static void main(String[] args) throws Throwable { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); }}本地方法棧
跟虛擬機(jī)棧類似,只是一個(gè)是虛擬機(jī)執(zhí)行Java方法,一個(gè)是執(zhí)行Native方法
異常狀況:
StackOverflowError 棧深度大于虛擬機(jī)所允許的深度
OOM
Java堆
線程共享的一塊內(nèi)存區(qū)域,從內(nèi)存回收角度來看,基本都采用分代收集算法,所以分為新生代、老年代。再細(xì)致一點(diǎn)可以分為Eden空間、From Survivor空間、To Survivor空間等。-Xmx -Xms控制堆空間大小
異常狀況:
1.OOM 堆無法擴(kuò)展時(shí)
/** * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * * java.lang.OutOfMemoryError: Java heap space */public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); } }}方法區(qū)
線程間共享。存儲已經(jīng)被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編輯器編譯后的代碼等數(shù)據(jù),在HotSpot虛擬機(jī)中可以稱為永生代。
運(yùn)行時(shí)常量在1.6及之前是方法區(qū)的一部分(String.intern()動態(tài)加入常量池) -XX:MaxPermSize控制大小。在JDK1.7及之后的版本是在Java堆中開辟的一塊內(nèi)存
異常狀況:
OOM
/*** 需要在JDK1.6上才能復(fù)現(xiàn),JDK1.7及之后版本的JVM已經(jīng)將運(yùn)行時(shí)常量池從方法區(qū)中移了出來,在Java 堆(Heap)中開辟了一塊區(qū)域存放運(yùn)行時(shí)常量池。* 在JDK1.7上運(yùn)行的效果則會一直執(zhí)行,直到堆內(nèi)存使用完畢* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M ** java.lang.OutOfMemoryError:PermGen space*/public class RuntimeConstantPoolOOM {public static void main(String[] args) { // 使用List保持著常量池引用,避免Full GC回收常量池行為 List<String> list = new ArrayList<String>(); // 10MB的PermSize在integer范圍內(nèi)足夠產(chǎn)生OOM了 int i = 0; while (true) { list.add(String.valueOf(i++).intern()); }}}/*** VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M* java.lang.OutOfMemoryError:PermGen space* 一直創(chuàng)建動態(tài)類*/public class JavaMethodAreaOOM {public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); }}static class OOMObject {}}直接內(nèi)存(不屬于虛擬機(jī)運(yùn)行時(shí)的數(shù)據(jù)區(qū)的一部分)
NIO可以使用Native函數(shù)庫直接分配對外的內(nèi)存,然后通過存儲在Java對中的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作。受限于機(jī)器物理內(nèi)存,可以通過-XX:MaxDirectMemorySize制定,如果不制定,默認(rèn)與Java堆最大值(-Xmx)一樣
異常狀況:
1.OOM
/** * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M * * java.lang.OutOfMemoryError */public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setaccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); while (true) { unsafe.allocateMemory(_1MB); } }}學(xué)習(xí)Java的同學(xué)注意了?。?! 學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號碼:183993990 我們一起學(xué)Java!
新聞熱點(diǎn)
疑難解答
圖片精選