JVM 各區域的用途以及潛在出現異常的示例
程式計數器
用於給位元組碼直譯器來選取嚇一跳需要執行的位元組碼指令。每個執行緒有一個獨立的程式計數器去,且各個執行緒之間互不影響。如果執行緒正在執行一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的記憶體地址;如果執行的是Native方法。在計數器為Undefined。此區域是JVM規範中唯一一個不存在OOM的區域
虛擬機器棧(區域性變數空間)
存放編譯器可知的各種基本資料型別(boolean、byte、char、short、int、float、long、double)、物件應用(reference)。64位的double、long佔用2個槽。記憶體空間在編譯期間就能確定,當進入一個方式時,這個方法需要分配的區域性變數空間是完全確定的,通過-Xss設定記憶體容量
異常狀況:
StackOverflowError 棧深度大於虛擬機器所允許的深度
OOM 如果虛擬機器棧可以動態擴充套件(當前大部分Java虛擬機器都可以動態擴充套件,只不過Java虛擬機器規範中的也允許固定長度的虛擬機器棧),如果擴充套件是無法申請到足夠的記憶體
在單個執行緒下,無論是猶豫棧幀太大還是虛擬機器棧容量太小,當記憶體無法分配的時候,虛擬機器丟擲的都是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; } } }
如果測試時不限於單執行緒,通過不斷的建立執行緒的方式到是可以產生記憶體溢位的異常。但是這樣產生的記憶體溢位異常與佔空間是否足夠大並不存在任何聯絡,或者準確的說,在這種情況下,為每個執行緒的棧分配的記憶體越大,反而越容易產生記憶體溢位的異常。
因為作業系統分配給每個程式的記憶體是有限制的,譬如32的window限制為2GB。此測試是通過建立大量的執行緒。每個執行緒佔用棧記憶體分配大量的記憶體,導致系統沒有足夠的記憶體。使其不能自動擴充套件
/** * VM Args:-Xss2M (這時候不妨設大些) * * 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(); } }
本地方法棧
跟虛擬機器棧類似,只是一個是虛擬機器執行Java方法,一個是執行Native方法
異常狀況:
StackOverflowError 棧深度大於虛擬機器所允許的深度
OOM
Java堆
執行緒共享的一塊記憶體區域,從記憶體回收角度來看,基本都採用分代收集演算法,所以分為新生代、老年代。再細緻一點可以分為Eden空間、From Survivor空間、To Survivor空間等。-Xmx -Xms控制堆空間大小
異常狀況:
1.OOM 堆無法擴充套件時
/** * 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()); } } }
方法區
執行緒間共享。儲存已經被虛擬機器載入的類資訊、常量、靜態變數、即時編輯器編譯後的程式碼等資料,在HotSpot虛擬機器中可以稱為永生代。
執行時常量在1.6及之前是方法區的一部分(String.intern()動態加入常量池) -XX:MaxPermSize控制大小。在JDK1.7及之後的版本是在Java堆中開闢的一塊記憶體
異常狀況:
OOM
/** * 需要在JDK1.6上才能復現,JDK1.7及之後版本的JVM已經將執行時常量池從方法區中移了出來,在Java 堆(Heap)中開闢了一塊區域存放執行時常量池。 * 在JDK1.7上執行的效果則會一直執行,直到堆記憶體使用完畢 * 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範圍內足夠產生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 * 一直建立動態類 */ 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 { } }
直接記憶體(不屬於虛擬機器執行時的資料區的一部分)
NIO可以使用Native函式庫直接分配對外的記憶體,然後通過儲存在Java對中的DirectByteBuffer物件作為這塊記憶體的引用進行操作。受限於機器實體記憶體,可以通過-XX:MaxDirectMemorySize制定,如果不制定,預設與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); } } }
相關文章
- JVM記憶體區域以及各區域的記憶體溢位異常,記憶體分代策略,垃圾收集演算法,各種垃圾收集器JVM記憶體溢位演算法
- JVM異常現象解析JVM
- [jvm]常見的oom異常JVMOOM
- 異常-throw的概述以及和throws的區別
- JVM(2)-Java記憶體區域與記憶體溢位異常JVMJava記憶體溢位
- JVM的記憶體區域JVM記憶體
- JVM(四)——OutOfMemoryError 異常JVMError
- Java記憶體區域與記憶體溢位異常(JVM學習系列1)Java記憶體溢位JVM
- JVM 異常退出的問題解決JVM
- 異常-編譯期異常和執行期異常的區別編譯
- 關於Java異常的分類示例Java
- firewalld: 各個zone的用途
- Java中Error和Exception的異同以及執行時異常(Runtime exception)與檢查型異常(checked exception)的區別JavaErrorException
- JVM區域劃分JVM
- JVM的記憶體區域劃分JVM記憶體
- Java異常處理的兩種方式以及自定義異常的使用方法Java
- 異常-自定義異常的實現和測試
- JVM系列(二) - JVM記憶體區域JVM記憶體
- 【JVM記憶體區域】JVM記憶體
- JVM 記憶體區域JVM記憶體
- JVM記憶體區域JVM記憶體
- 深入理解JVM讀書筆記一: Java記憶體區域與記憶體溢位異常JVM筆記Java記憶體溢位
- 異常以及異常處理框架探析框架
- Python中各種括號的區別、用途及使用方法Python
- JVM系列之一 JVM的基礎概念與記憶體區域JVM記憶體
- jFreeChart在Linux下的問題以及常見異常Linux
- 異常中的異常——藉助系統異常處理特例實現匪夷所思的漏洞利用
- 淺談 JVM GC 的安全點與安全區域JVMGC
- JVM(一)——Java 執行時的資料區域JVMJava
- 前端開發中的Error以及異常捕獲前端Error
- JVM實戰調優(空格引發的服務異常)JVM
- JAVA記憶體區域與記憶體溢位異常Java記憶體溢位
- 區域性性原理——各類優化的基石優化
- Java異常十一:使用throw丟擲異常物件;throw和throws的區別Java物件
- JVM 系列文章之 Java 的記憶體區域JVMJava記憶體
- 小白也能看懂的JVM記憶體區域JVM記憶體
- JVM(二)-記憶體區域之執行緒私有區域JVM記憶體執行緒
- subversion 安裝各種異常 之 SQLiteSQLite