java ppt转图片 内存溢出_Java虚拟机内存及内存溢出异常
Java與C++之間有一堵由內存動態分配和垃圾收集技術所圍成的高墻,墻外面的人想進來,墻里面的人卻想出來。
不知道其他人想出來沒,反正我是沒想出來,為什么這個JVM的運行時內存要這樣設計?。
以下內容中,Java虛擬機(JVM)特指HotSpot虛擬機。
JVM啟動后,整體來看,會把向操作系統申請到的內存空間分成這樣幾個部分:
它們在《Java虛擬機規范》中都有相關規定和說明,只是有的區域具體實現細節根據Java虛擬機實現者來決定,《Java虛擬機規范》不做強行要求,例如方法區。下面對這幾個部分進行詳細敘述,在敘述過程中,需要不斷參考下面這個示意圖來幫助理解。
Java運行時內存布局文中示例程序來自
《深入理解Java虛擬機》
周志明·著
如有雷同,不是巧合 ?
1. 程序計數器區(Program Counter Register)
這個區域內容指向當前線程所執行字節碼行號,是線程私有區域。和操作系統里面的PC指針類似,只不過這個PC指針指向當前執行的字節碼行號,操作系統的指針指向下一條執行的指令地址。
需要額外注意的是,如果當前線程在執行Java程序,那么如上所述指向字節碼行號,如果執行的是Native方法,那么這個區域是空(Undefined)的。
該區域也是唯一一個《Java虛擬機規范》里面沒有 OutOfMemoryError 錯誤的定義的區域。
2. Java虛擬機棧(VM Stack)
該區域的內容是Java方法執行的線程內存模型,具體來說,存放的是棧幀,在棧幀中,存放了局部變量表、操作數棧等信息,當一個方法被調用時,就會創建一個棧幀來記錄信息,方法從被調用到返回的過程,也就對應了棧幀從入棧(虛擬機棧)到出棧的過程。顯而易見,這個區域必須是線程私有的,否則就混亂了呀?。
這個區域在《Java虛擬機規范》中,詳細定義了以下內容:
2.1 虛擬機棧異常示例程序
/**?*?VM?Args:?-Xss128k
?*?-Xss:?減小棧內存容量
?*?虛擬機棧和本地方法棧測試,拋出?StackOverflow?異常
?*/
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;
????????}
????}
}
對于 OutOfMemoryError 就需要格外小心了,程序如下:
/**?*?!!?需要先保存當前系統的工作,因為這個程序比較危險。
?*?可能會造成系統假死或者其他異常(甚至強制重啟才可以使用)。
?*?
?*?VM?Args:?-Xss2m
?*?-Xss:?減少棧內存容量
?*?使用線程來制造內存溢出異常
?*/
public?class?JavaVMStackOOM?{
????private?void?dontStop()?{?while?(true)?{}?}
????public?void?stackLeakByThread()?{
????????while?(true)?{
????????????Thread?thread?=?new?Thread(new?Runnable()?{
????????????????public?void?run()?{?dontStop();?}
????????????});
????????????thread.start();
????????}
????}
????public?static?void?main(String[]?args)?{
????????JavaVMStackOOM?oom?=?new?JavaVMStackOOM();
????????oom.stackLeakByThread();
????}
}
3. 本地方法棧(Native Method Stack)
本地方法棧“雷同”虛擬機棧的功能,唯一的區別在于前者記錄的是本地方法的執行記錄,后者是Java程序的執行記錄。
因為這個區域在《Java虛擬機規范》中并沒有詳細規定本地方法的實現語言和數據結構,所以虛擬機可以根據自己需要來進行擴展。只不過,在HotSpot虛擬機中,是把虛擬機棧和本地方法棧統一放到一起了的。
在可能拋出的異常類型上,和虛擬機棧一樣樣:
4. 堆(Heap)
此堆非彼堆,不是同一堆?。上篇文章說到的《探索STL:堆數據結構及算法》,和這里的堆完全不是同一個概念。更何況,這個堆是Java虛擬機的東西,那個是C++的東西,兩個混淆了不大好吧?。
Java堆可以說是整個Java虛擬機中最靈活的地方了,也是最值得研究的地方了,目前好多研究都是圍繞這個區域的垃圾收集(Garbage Collection, GC)展開的。這個區域在Java虛擬機啟動之初就創建好了,給所有線程共享使用,這里存放的唯一內容就是對象實例,拿《Java虛擬機規范》中的描述來說就是:
The heap is the runtime data area from which memory for all class instances and arrays is allocated.
這一塊內存是設計成可擴展的,使用參數 -Xmx 和 -Xms 來設定允許的最大內存和允許的最小內存。當內存不夠完成實例的創建也不能完成內存擴展時,將會拋出 OutOfMemoryError 異常。
4.1 堆內存溢出程序示例
import?java.util.ArrayList;import?java.util.List;
/**
?*?-Xms?設置最小值
?*?-Xmx?設置最大值
?*?-XX:+HeapDumpOnOutOfMemoryError??當出現內存溢出異常時,Dump出當前的內存轉儲快照
?*?VM?Args:?-Xms20m?-Xmx20m?-XX:+HeapDumpOnOutOfMemoryError
?*?
?*?堆內存溢出異常測試
?*/
class?HeapOOM?{
????static?class?OOMObject?{}
????public?static?void?main(String[]?args)?{
????????List?list?=?new?ArrayList();while?(true)?{
????????????list.add(new?OOMObject());
????????}
????}
}
5. 方法區(Method Area)
這個區域也是線程共享區域,里面存放了被虛擬機加載進來的:
- 類信息
- 常量、靜態變量
- 即時編譯后的程序
需要注意的是在這里有一個版本分歧,JDK6及以前的版本,方法區使用分代設計來實現該區域,但是JDK7就開始不一樣了,首先是在JDK7吧放在永久代的字符串常量池、靜態變量移出到了本地內存中,到了JDK8,徹底將方法區的分代設計拋棄了,換成了使用本地內存實現的“元空間”代替。
這個空間在不能滿足新的內存分配需求時,拋出 OutOfMemoryError 異常。
4.1 方法區內存溢出示例程序
import?net.sf.cglib.proxy.Enhancer;import?net.sf.cglib.proxy.MethodInterceptor;
import?net.sf.cglib.proxy.MethodProxy;
import?java.lang.reflect.Method;
/**
?*?VM?Args:?-XX:PermSize=10m?-XX:MaxPermSize=10m?-XX:+HeapDumpOnOutOfMemoryError
?*?上面這個設置是沒用的,因為從JDK8開始,HotSpot虛擬機就沒有了永久代這么一說,變成了元空間可通過這樣設置來實現:
?*?VM?Args:?-XX:MaxMetaspaceSize=10m
?*?-XX:MaxMetaspaceSize=10m?設置最大元空間,默認是-1,即不設置大小(受限于本地)
?*?-XX:MetaspaceSize=10m????設置元空間的初始空間大小,以字節為單位,如果達到了該值,就會觸發垃圾收集,并且會調整元空間的大小
?*?-XX:MinMetaspaceFreeRatio
?*?報錯為:Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
?*/
public?class?JVMMethodAreaOOM?{
????static?class?OOMObject?{}
????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?o,?Method?method,?Object[]?objects,?MethodProxy?methodProxy)?throws?Throwable?{
????????????????????return?methodProxy.invokeSuper(o,?objects);
????????????????}
????????????});
????????????enhancer.create();
????????}
????}
}
5.1 運行時常量池(Runtime Constant Pool)
這是一個神奇的區域?
首先它是方法區的一個部分,在類文件中有一部分內容是常量池表,這部分內容存放了在編譯器生成的字面量和符號引用,在類加載后放到方法區的運行時常量中。
運行時常量池具備動態特性,也就是說可以將運行期產生的常量放到池子中,例如 String.intern() 方法。
同方法區異常一樣,在內存申請不足的時候,會拋出 OutOfMemoryError 異常。
5.1.1 運行時常量池異常示例程序
import?java.util.HashSet;import?java.util.Set;
/**
?*?VM?Args:?-Xmx2m
?*?因為JDK8已經把原本放在永久代的字符串常量池移到了?Java堆?中,
?*?所以限制了堆大小之后,會模擬出來堆內存溢出異常
?*/
public?class?RuntimeConstantPoolOOM?{
????public?static?void?main(String[]?args)?{
????????Set?set?=?new?HashSet();short?i?=?0;while?(true)?{
????????????set.add(String.valueOf(i++).intern());
????????}
????}
}
6. 直接內存(Direct Memory)
如果查《Java虛擬機規范》,并不能夠找到這部分內容,也就是說它并不是虛擬機的一部分,但是這部分內容經常被使用到,舉個例子:JDK1.4之后出現的NIO(New Input/Output),記起來了吧??這個基于緩沖區(Buffer)和通道(Channel)的I/O模型。使用本地函數直接分配堆外內存,然后通過 DirectByteBuffer 對象作為申請到的堆外內存引用,可以省去在Java堆和本地內存中來回復制數據。
很明顯,既然不屬于Java虛擬機的范疇,自然也就不會被Java虛擬機大小所限制,不過,仍然要受到本機內存的限制。當內存申請失敗時,會拋出 OutOfMemoryError 異常。
6.1 直接內存異常示例程序
import?sun.misc.Unsafe;import?java.lang.reflect.Field;
/**
?*?VM?Args:?-Xmx20m?-XX:MaxDirectMemorySize=10m
?*?-XX:MaxDirectMemorySize??設置最大直接內存,默認是和Java堆保持一致
?*/
public?class?DirectMemoryOOM?{
????private?static?final?int?_1MB?=?1024?*?1024;
????public?static?void?main(String[]?args)?throws?IllegalAccessException?{
????????Field?unsafeField?=?Unsafe.class.getDeclaredFields()[0];
????????unsafeField.setAccessible(true);
????????Unsafe?unsafe?=?(Unsafe)?unsafeField.get(null);
????????while?(true)?{
????????????unsafe.allocateMemory(_1MB);
????????}
????}
}
好了,圖里面出現的各個區域所能出現的問題,已經差不多都遇到了(這種主動犯錯的機會可不多?),也該休息休息然后去復習考試了嗚嗚嗚嗚嗚?……
總結
以上是生活随笔為你收集整理的java ppt转图片 内存溢出_Java虚拟机内存及内存溢出异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++学习笔记之成员函数
- 下一篇: java美元兑换,(Java实现) 美元