第2章 Java記憶體地區與記憶體溢出異常–《深入理解 JAVA 虛擬機器》筆記

來源:互聯網
上載者:User

1概述

基本就是C和C++程式編寫需要維護沒一個對象生命開始到終結。

Java把記憶體控制權利交給了JAVA 虛擬機器,但是有時卻會出現記憶體溢出等問題。

2運行時資料區域

Java虛擬機器在執行Java程式的過程中會把他所管理的記憶體換分為若干不同的記憶體地區。


===運行時資料區====
方法區(Method Area)   虛擬機器棧(VM Stack)   本地方法棧(Native Method Stack)
堆(Heap)               程式計算機(Program Counter Register)
===運行時資料區====
↓↑ ↓↑
     執行引擎 →
本地庫介面  →    本地方法庫
     
方法區和堆:由所有線程共用的資料區
其他區:線程隔離的資料區

1、程式計數器:
程式計數器(Program Counter Register)是一塊較小的記憶體空間,他的作用可以看做是當前線程所執行的位元組碼的行數指標。
由於Java虛擬機器的多線程是通過線程輪流切換並分配處理機執行時間的方式來實現的,在任何一個確定的時刻,一個處理器只會執行一條線程上的指令。
因此,為了線程切換後能恢複到正常的執行位置,每條線程都需要有一條單獨的程式計數器,各條線程之間的計數器互不影響,隔離儲存區 (Isolated Storage),我們成這個記憶體地區為“線程私人”的記憶體。
此記憶體地區是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError情況的地區。

2、Java虛擬機器棧
與程式設計器一樣,Java虛擬機器棧(Java Virtual Machine Stacks)也是線程私人的,他的生命週期和線程相同。
虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會被建立一個棧幀(Stack Frame)用於儲存局部變數表、操作棧、動態連結、方法出口等資訊。
每個方法被調用直至執行完成的過程,就對應這一個棧幀在虛擬機器棧中從入棧到出棧的過程。

在Java虛擬機器規範中,對這個地區規定了兩種異常情況:
如果線程請求的棧深度大於虛擬機器所允許的深度,講拋出StackOverflowError異常;
如果虛擬機器可以動態擴充,當擴充時無法申請到足夠的記憶體時會拋出OutOfMemoryError異常。

3、本地方法棧
本地方法棧(Native Method Stacks)與虛擬機器棧所發揮的作用是非常相似的,其區別不過是虛擬機器棧為虛擬機器執行方法(也就是位元組碼)服務,而本地方法棧為虛擬機器使用Native方法服務。

4、Java堆
對於大多數應用來說,Java堆(Java Heap)是Java虛擬機器所管理的記憶體中最大的一塊。
Java堆是被所有線程共用的一塊記憶體地區,在虛擬機器啟動時建立。
此記憶體地區存在的唯一目的就是存放對象執行個體,幾乎所有的對象執行個體都在這裡分配記憶體。

Java堆是垃圾收集器管理的主要區域,因此很多時候也被成為“GC”堆(Collected Heap)。
還有一些劃分是為了更好的回收記憶體,或者更快的分配記憶體,如:Eden控制項、From Survivor空間、To Survivor空間等。

一般主流的虛擬機器都可以控制擴充這塊地區的大小(-Xmx 和 -Xms)。
如果在堆中沒有記憶體完成實際分配,並且堆無法再擴充,將會拋出OutOfMemoryError異常。

5、方法區
方法區(Method Area)與Java堆一樣,是各個線程共用的記憶體地區,他用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的代碼等資料。是堆的一個邏輯部分。

這個地區的記憶體回收目標主要是針對常量池的回收和對類型的卸載。

也有OutOfMemoryError。

6、運行時常量池
運行時常量池(Runtime Constant Pool)是方法區的 一部分。用於儲存編譯期產生的各種字面量和符號引用。

也有OutOfMemoryError。

7、直接記憶體
直接記憶體(Direct Memory)並不是虛擬機器運行時資料區的一部分。
用於提高效能,如NIO(New Input/Output)類,緩衝區(Buffer)之類的。

3對象訪問

例子:
Object obj = new Object();
假設這句代碼出現在方法體中,那“Object obj”這部分的語義將會反映到Java棧的本地變數表中,作為一個reference類型資料出現。
而“new Object()”這部分的語義將會反映到Java堆中,形成一個儲存了Object類型所有執行個體資料值(Instance Data)的結構化記憶體。
另外,在Java堆中,還必須包含能尋找到此物件類型資料(如物件類型、父類、實現的介面、方法等)的地址資訊,這些類型資料則儲存在方法區中。

reference對方反問方式主要有兩種:使用控制代碼和直接指標。
使用控制代碼訪問方式,Java堆中將會劃分出一塊記憶體作為控制代碼池,reference中儲存的就是對象的控制代碼地址,而控制代碼中包含了對象執行個體資料和類型資料各自的具體地址資訊。
如果使用直接指標訪問方式,Java堆對象的布局中就必須考慮如何放置訪問類型的資料的相關資訊,reference中直接儲存的就是對象地址。

4實戰:OutOfMemoryError異常

1、Java堆溢出
java.lang.OutOfMemoryError: Java heap space
Java堆用於儲存物件執行個體,我們只要不斷的建立對象,並且保證GC Roots到對象之間有可達路徑來避免記憶體回收機制清楚這些對象,就會在數量達到最大堆的容量限制後產生記憶體溢出異常。

Demo:
先配置DebugVM

//VM arguments://-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8package cn.partner4java.vm;import java.util.ArrayList;import java.util.List;/** * VM args:-Xmx20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError *  * @author partner4java *  */public class HeapOOM {static class OOMObject {}public static void main(String[] args) {List<OOMObject> list = new ArrayList<HeapOOM.OOMObject>();int i = 0;try {while (true) {i++;list.add(new OOMObject());}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("i=" + i);}//後台列印://[GC [DefNew: 8192K->1024K(9216K), 0.0175111 secs] 8192K->4442K(19456K), 0.0175397 secs]// [GC [DefNew: 6433K->1024K(9216K), 0.0241072 secs] 9852K->9737K(19456K), 0.0241371 secs]// [GC [DefNew: 7581K->7581K(9216K), 0.0000196 secs][Tenured: 8713K->10240K(10240K), 0.0482572 secs] 16294K->11919K(19456K), 0.0483254 secs]// [Full GC [Tenured: 10240K->7992K(10240K), 0.0443166 secs] 18432K->14504K(19456K), [Perm : 2060K->2060K(12288K)], 0.0443550 secs]// [Full GC [Tenured: 8593K->8593K(10240K), 0.0532794 secs] 17809K->17809K(19456K), [Perm : 2060K->2060K(12288K)], 0.0533182 secs]// [Full GC [Tenured: 8593K->8590K(10240K), 0.0618192 secs] 17809K->17806K(19456K), [Perm : 2060K->2058K(12288K)], 0.0618540 secs]// i=1507964// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space// at java.util.Arrays.copyOf(Arrays.java:2760)// at java.util.Arrays.copyOf(Arrays.java:2734)// at java.util.ArrayList.ensureCapacity(ArrayList.java:167)// at java.util.ArrayList.add(ArrayList.java:351)// at cn.partner4java.vm.HeapOOM.main(HeapOOM.java:23)// Heap//  def new generation   total 9216K, used 9216K [0x04550000, 0x04f50000, 0x04f50000)//   eden space 8192K, 100% used [0x04550000, 0x04d50000, 0x04d50000)//   from space 1024K, 100% used [0x04d50000, 0x04e50000, 0x04e50000)//   to   space 1024K,   0% used [0x04e50000, 0x04e50000, 0x04f50000)//  tenured generation   total 10240K, used 8598K [0x04f50000, 0x05950000, 0x05950000)//    the space 10240K,  83% used [0x04f50000, 0x057b58c8, 0x057b5a00, 0x05950000)//  compacting perm gen  total 12288K, used 2079K [0x05950000, 0x06550000, 0x09950000)//    the space 12288K,  16% used [0x05950000, 0x05b57d58, 0x05b57e00, 0x06550000)// No shared spaces configured.}}

錯誤: java.lang.OutOfMemoryError: Java heap space

首先分辨出是出現了記憶體流失(Memory Leak)還是記憶體溢出(MemoryOverflow)

2、虛擬機器棧和本地方法棧溢出
如果線程請求的棧深度大於虛擬機器所允許的最大深度,將拋出StackOverflowError異常。
如果虛擬機器在擴充棧時無法申請到足夠的記憶體空間,則拋出OutOfMemoryError異常。
Demo:

java.lang.StackOverflowError:package cn.partner4java.vm;/** * VM args:-Xss128K * @author partner4java * */public class JavaVMStackSOF {private int stackLength = 1;private void stackLeak() {stackLength++;stackLeak();}public static void main(String[] args) throws Exception {JavaVMStackSOF stackSOF = new JavaVMStackSOF();try {stackSOF.stackLeak();} catch (Exception e) {//throw e;}finally{System.out.println("stack length:" + stackSOF.stackLength);}//後台列印://stack length:7651//java.lang.StackOverflowError}}

OutOfMemoryError:
java.lang.OutOfMemoryError: unable to create new native thread

package cn.partner4java.vm;/** * VM args:-Xss2M * @author partner4java * */public class JavaVMStackOOM {private void dontStop() {while(true){}}private void stackLeakByThread() {while(true){Thread thread = new Thread(){public void run() {dontStop();};};thread.start();}}public static void main(String[] args) {JavaVMStackOOM stackOOM = new JavaVMStackOOM();stackOOM.stackLeakByThread();//後台列印://java.lang.OutOfMemoryError: unable to create new native thread}}

3、運行時常量池溢出
java.lang.OutOfMemoryError: PermGen space

Demo:

package cn.partner4java.vm;import java.util.ArrayList;import java.util.List;/** * VM args:-XX:PermSize=10M -XX:MaxPermSize=10M * @author partner4java * */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());}//後台列印://java.lang.OutOfMemoryError: PermGen space}}

4、方法區溢出
方法區用於存放Class的相關資訊,如類名、存取修飾詞、常量池、欄位描述、方法描述等。
java.lang.OutOfMemoryError: PermGen space
Demo:

package cn.partner4java.vm;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;/** * VM args: -XX:PermSize=10M -XX:MaxPermSize=10M * @author partner4java * */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() {@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {return proxy.invoke(obj, args);}});enhancer.create();}}//後台列印://java.lang.OutOfMemoryError: PermGen spacestatic class OOMObject {}}

5、本機直接記憶體溢出
DirectMemory容量可通過-XX:MaxDirectMemorySize指定,預設與Java堆的最大值(-Xmx指定)一樣。
java.lang.OutOfMemoryError

Demo:

package cn.partner4java.vm;import java.lang.reflect.Field;import sun.misc.Unsafe;/** * VM args: -Xmx20M -XX:MaxDirectMemorySize=10M * @author partner4java * */public class DirectMemoryOOM {private static final int _1MB = 1024*1024;public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while(true) {unsafe.allocateMemory(_1MB);}//後台列印://java.lang.OutOfMemoryError}}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.