jvm的內存溢出異常


      1.jvm的內部體系結構淺析

      2.jvm的幾個運行時數據區域

      3.jvm的內存溢出異常

    在Java虛擬機規范的描述中,除了PC(程序計數器)寄存器外,虛擬機內存的其他幾個運行時區域都有發生OutOfMemoryError異常的可能。當發生OutOfMemoryError異常時,無法用try...catch捕捉。

    在開始講解之前,在這里先簡單介紹下虛擬機啟動相關的一些內存設置參數。因為OutOfMemoryError異常發生,與這些參數的設置密切相關。

舉例說明含義:

-Xss128k

每個線程的java棧大小,一個線程java棧所有棧幀大小總和最大允許的尺寸128k。
-Xms128m
表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配。

-XX:PermSize=20M

設置方法區的初始大小

-XX:MaxPermSize=30M

設置方法區的最大值

 Java棧溢出

在Java虛擬機規范中,對這個區域規定了兩種異常狀況:StackOverflowError和OutOfMemoryError異常。

 1.StackOverflowError異常

       每當java程序代碼啟動一個新線程時,Java虛擬機都會為它分配一個Java棧。Java棧以幀為單位保存線程的運行狀態。當線程調用java方法時,虛擬機壓入一個新的棧幀到該線程的java棧中。只要這個方法還沒有返回,它就一直存在。如果線程的方法嵌套調用層次太多(如遞歸調用),隨着java棧中幀的逐漸增多,最終會由於該線程java棧中所有棧幀大小總和大於-Xss設置的值,而產生StackOverflowError內存溢出異常。例子如下:

 1 /**
 2  * VM Args: -Xss128k
 3  */
 4 public class Test {
 5     
 6     private int count = 0;
 7 
 8     public static void main(String[] args) {
 9         new Test().method();
10     }
11     
12     public void method() {
13         System.out.println(++count);
14         method();
15     }
16 
17 }

 -Xss為128k。其中的一次測試結果為,當count的值累加到2312時,發生如下異常:

Exception in thread "main" java.lang.StackOverflowError
    at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:58)
    at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:392)
    at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:447)
    at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:544)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:252)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:106)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
    at java.io.PrintStream.write(PrintStream.java:476)
    at java.io.PrintStream.print(PrintStream.java:547)
    at java.io.PrintStream.println(PrintStream.java:686)
    at jvm.Test.method(Test.java:17)

 修改-Xss為1280k。其中的一次測試結果為,當count的值累加到26888時,發生StackOverflowError異常。隨着-Xss參數值的增大,可以嵌套的方法調用層次也相應增加。

綜上所述,StackOverflowError異常是由於方法調用的層次太深,最終導致為某個線程分配的所有棧幀大小總和大於-Xss設置的值,從而發生StackOverflowError異常。

2.OutOfMemoryError異常

java程序代碼啟動一個新線程時,沒有足夠的內存空間為該線程分配java棧(一個線程java棧的大小由-Xss參數確定),jvm則拋出OutOfMemoryError異常。例子如下:

 1 /**
 2  * VM Args: -Xss128k
 3  */
 4 public class Test {
 5     
 6     public static void main(String[] args) {
 7         int count = 0;
 8         while (true) {
 9             Thread thread = new Thread(new Runnable() {
10                 public void run() {
11                     while (true) {
12                         try {
13                             Thread.sleep(5000);
14                         } catch (Exception e) {}
15                     }
16                 }
17             });  
18             thread.start();
19             System.out.println(++count);
20         }
21     }
22 
23 }

 -Xss為128k。其中的一次測試結果為,當count的值累加到11887時,發生如下異常:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:640)
    at jvm.Test.main(Test.java:20)

 修改-Xss為1280k。其中的一次測試結果為,當count的值累加到1270時,發生OutOfMemoryError異常。隨着-Xss參數值的增大,java程序可以創建的總線程數越少。

 Java堆溢出

Java堆用於儲存對象實例。當需要為對象實例分配內存,而堆的內存占用又已經達到-Xmx設置的最大值。將會拋出OutOfMemoryError異常。例子如下:

 1 /**
 2  * VM Args: -Xmx5m
 3  */
 4 public class Test {
 5     
 6     public static void main(String[] args) {
 7         int count = 0;
 8         List<Object> list = new ArrayList<Object>();
 9         while (true) {
10             list.add(new Object());
11             System.out.println(++count);
12         }
13     }
14 
15 }

 -Xmx為5m。其中的一次測試結果為,當count的值累加到297868時,發生如下異常:

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 jvm.Test.main(Test.java:15)

 修改-Xmx為10m。其中的一次測試結果為,當count的值累加到670205時,發生OutOfMemoryError異常。隨着-Xmx參數值的增大,java堆中可以存儲的對象也越多。

 方法區溢出

方法區用於存放java類型的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。在類裝載器加載class文件到內存的過程中,虛擬機會提取其中的類型信息,並將這些信息存儲到方法區。當需要存儲類信息而方法區的內存占用又已經達到-XX:MaxPermSize設置的最大值。將會拋出OutOfMemoryError異常。對於這種情況的測試,基本的思路是運行時產生大量的類去填滿方法區,直到溢出。這里需要借助CGLib直接操作字節碼運行時,生成了大量的動態類。例子如下:

 1 /**
 2   * VM Args: -XX:MaxPermSize=50M
 3   */
 4  public class Test {
 5      
 6      public static void main(String[] args) {
 7          int count = 0;
 8          while (true) {
 9               Enhancer enhancer = new Enhancer();
10               enhancer.setSuperclass(Test.class);
11               enhancer.setUseCache(false);
12               enhancer.setCallback(new MethodInterceptor() {
13                  public Object intercept(Object obj, Method method, Object[] args, 
14                    MethodProxy proxy) throws Throwable {
15                      return proxy.invoke(obj, args);
16                  }
17               });
18               enhancer.create();
19               System.out.println(++count);
20          }
21      }
22  
23  }

-XX:MaxPermSize為50m。其中的一次測試結果為,當count的值累加到3953時,發生如下異常:

Caused by: java.lang.OutOfMemoryError: PermGen space
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    ... 8 more

修改-XX:MaxPermSize為100m。其中的一次測試結果為,當count的值累加到8022時,發生OutOfMemoryError異常。隨着-XX:MaxPermSize參數值的增大,java方法區中可以存儲的類型數據也越多。

 

相關的參考資料:

1.深入Java虛擬機(原書第2版)

2.深入理解Java虛擬機:JVM高級特性與最佳實踐

3.互聯網相關的文章


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM