1. 程式人生 > >Java記憶體溢位的幾種情況

Java記憶體溢位的幾種情況

正文

本文通過幾段程式碼模擬實際的記憶體溢位異常。

文中程式碼都是基於Oracle公司的HotSpot虛擬機器執行的。

1. Java堆溢位

1.1 模擬場景

Java堆用於儲存物件,只要不斷的建立物件,並保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清除這些物件,

那麼在物件數量到達最大堆的容量限制後就會產生記憶體溢位異常。

複製程式碼
package com.lindaxuan.outofmemory;

import java.util.ArrayList;
import java.util.List;

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * 將堆的最小值-Xms引數與最大值-Xmx引數設定為一樣即可避免堆自動擴充套件。
 * 
@author linxuan */ public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); } } } /* result: java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid9220.hprof ... Heap dump file created [27717826 bytes in 0.160 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2245) at java.util.Arrays.copyOf(Arrays.java:2219) at java.util.ArrayList.grow(ArrayList.java:242) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208) at java.util.ArrayList.add(ArrayList.java:440) at com.lindaxuan.outofmemory.HeapOOM.main(HeapOOM.java:19) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
*/
複製程式碼

1.2 用記憶體影響分析工具分析堆快照

回到頂部

回到頂部

2.虛擬機器棧和本地方法棧溢位

HotSpot虛擬機器中不區分虛擬機器棧和本地方法棧。棧容量用-Xss引數設定。Java虛擬機器規範中描述了兩種異常:

  • 如果執行緒請求的棧深度大於虛擬機器鎖允許的最大深度,將丟擲StackOverflowError異常。
  • 如果虛擬機器在擴充套件棧時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常。

2.1 StackOverflowError異常

複製程式碼
package com.lindaxuan.outofmemory;

/**
 * VM Args:-Xss128k
 * Error: Could not create the Java Virtual Machine.

 Error: A fatal exception has occurred. Program will exit.
 The stack size specified is too small, Specify at least 160k

 VM Args:-Xss256k
 * 
@author linxuan */ 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; } } } /* result: stack length:1868 Exception in thread "main" java.lang.StackOverflowError at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:18) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) at com.lindaxuan.outofmemory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:19) ... */
複製程式碼

當單個執行緒下,無論是由於棧幀太大還是虛擬機器棧容量太小,當記憶體無法分配的時候,虛擬機器丟擲的都是StackOverflowError異常。

2.2 OutOfMemoryError異常

複製程式碼
package com.lindaxuan.outofmemory;

/**
 * VM Args:-Xss2M (這時候不妨設大些)
 * @author linxuan
 */
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) throws Throwable {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}
/*
my result:
run too long
 */
複製程式碼回到頂部

3.方法區和執行時常量池溢位

3.1 執行時常量區溢位

下面這段程式碼需要jdk1.6模擬。

複製程式碼
package com.lindaxuan.outofmemory;

import java.util.ArrayList;
import java.util.List;

/**
 * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
 * @author zzm
 * could not download jdk1.6 for macos
 */
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());
        }
    }
}
/*
result:
run too long
 */
複製程式碼

String.intern()返回引用的測試

複製程式碼
package com.lindaxuan.outofmemory;

public class RuntimeConstantPoolOOM2 {

    public static void main(String[] args) {
        String str1 = new StringBuilder("中國").append("釣魚島").toString();
        System.out.println(str1.intern() == str1);

        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);
    }
}
/*
result:
true
false
 */
複製程式碼

對於jdk1.6,intern()方法會把首次遇到的字串例項複製到永久代中,返回的也是永久代中這個字串例項的引用。

而StringBuilder建立的字串例項在Java堆,所以必然不是同一個引用,將返回false。

而jdk1.7中的intern()實現不會複製例項,只是在常量池中首次出現的例項引用,因此intern()返回的引用和由StringBuild建立的那個字串例項是同一個。

3.2 String建立物件和對應記憶體狀態

再看另一段程式碼

複製程式碼
public class StringConstantPool {
    public static void main(String[] args) {

        String str1 = new StringBuilder("中國").append("釣魚島").toString();
        System.out.println(str1.intern() == str1);

        String str2 = new String("倚天不屠龍");
        System.out.println(str2.intern() == str2);
    }
}
/*
true
false
 */
複製程式碼

“中國釣魚島”和“倚天不屠龍”都在常量區中不存在,那麼為什麼輸出結果一個是true,另一個是false呢?

這就涉及到建立String物件的原理。下面我們將程式碼和記憶體對應起來看一下。

String str1 = new StringBuilder("中國").append("釣魚島").toString(); ////String建立物件時,會把引數"中國"和“釣魚島”放到常量池中

記憶體狀態1

System.out.println(str1.intern() == str1); // str1.intern()將str1的引用複製到常量池中

記憶體狀態2

String str2 = new String("倚天不屠龍"); //String建立物件時,會把引數"倚天不屠龍"放到常量池中

記憶體狀態3

System.out.println(str2.intern() == str2); // str2.intern()先去常量池中看有沒有"倚天不屠龍",已經有了。

記憶體狀態4 (和記憶體狀態3一致)

3.3 執行時方法區溢位

下面一段程式碼藉助CGLib使方法區出現記憶體溢位異常。

複製程式碼
package com.lindaxuan.outofmemory;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * @author linxuan
 */
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 {

    }
}
/*
 * result:
 Exception in thread "main"
 Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
*/
複製程式碼回到頂部

4.本機直接記憶體溢位

DirectMemory容量可通過-XX: MaxDirectMemorySize指定,如果不指定,則預設與Java堆最大值 (-Xmx指定)一樣。

複製程式碼
package com.lindaxuan.outofmemory;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
 * @author linxuan
 */
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);
        }
    }
}
複製程式碼

以上是模擬各種型別的記憶體溢位異常。

注:

本文的程式碼基於於深入理解Java虛擬機器 2.4 實戰:OutOfMemoryError異常,有輕微調整。

虛擬機器的引數根據機器效能不同可以靈活調整。

5. JVM相關wiki和工具

相關推薦

記憶體溢位情況

Tomcat記憶體溢位的原因 在生產環境中tomcat記憶體設定不好很容易出現記憶體溢位。造成記憶體原因是不一樣的,當然處理方式也不一樣。 這裡根據平時遇到的情況和相關資料進行一個總結。常見的一般會有下面三種情況: 1.OutOfMemoryError: Java h

java記憶體溢位的常見情況和處理方式總結

注:建議如果是面試,只用說第一部分就OK,個人見解,視情況而定。一:總結:建議面試時可以簡略說java.lang.OutOfMemoryError這個錯誤是開發中經常遇到的錯誤,產生該錯誤的原因大都出於

java記憶體洩漏5情況總結】

記憶體洩漏定義:一個不再被程式使用的物件或變數還在記憶體中佔有儲存空間。由於java的JVM引入了垃圾回收機制,垃圾回收器會自動回收不再使用的物件,瞭解JVM回收機制的都知道JVM是使用引用計數法和可達性分析演算法來判斷物件是否是不再使用的物件,本質都是判斷一個物件是否還被引

Java記憶體溢位情況

正文本文通過幾段程式碼模擬實際的記憶體溢位異常。文中程式碼都是基於Oracle公司的HotSpot虛擬機器執行的。1. Java堆溢位1.1 模擬場景Java堆用於儲存物件,只要不斷的建立物件,並保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清除這些物件,那麼在物

JVM之記憶體溢位情況以及可以採取的解決方案

開發中遇到過以下三種記憶體溢位的狀況: 一、 java.lang.OutOfMemoryError: Java heap space 二、 java.lang.OutOfMemoryError: PermGen space 三、 java.lang.OutO

Jvm記憶體溢位情況

1、java堆溢位 java對用於儲存物件的例項,只要不斷的建立物件,並且保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清除這些物件,那麼在物件數量達到最大堆的容量限制之後機會產生記憶體溢

異常、堆記憶體溢位、OOM的情況

1、堆記憶體溢位 【情況一】:   java.lang.OutOfMemoryError: Java heap space:這種是java堆記憶體不夠,一個原因是真不夠,另一個原因是程式中有死迴圈;   如果是java堆記憶體不夠的話,可以通過調整J

JAVA記憶體洩漏的情況

Java記憶體洩漏引起的原因:   記憶體洩漏是指無用物件(不再使用的物件)持續佔有記憶體或無用物件的記憶體得不到及時釋放,從而造成記憶體空間的浪費稱為記憶體洩漏。   長生命週期的物件持有短生命週期物件的引用就很可能發生記憶體洩漏,儘管短生命週期物件已經不再需要,但是因

java.lang.NullPointerException報錯的情況

style == poi 就會 string arr 不存在 判斷 對象 java.lang.NullPointerException報錯的幾種情況: 1.字符串變量未初始化; 2.接口類型的對象沒有用具體的類初始化,比如:   List stuList ;這種情況就會報空

java中出現內存溢出的情況

xss 除了 占用 memory 兩個 存在 text spa 調用 情況一:java.lang.OutOfMemoryError: Java heap space 原因:java堆內存不足,可能是真的不足,也可能是程序中有死循環 方案:1、調整JVM參數-Xms2048m

關於Java空指標異常的情況的總結

1:NullPointerException由RuntimeException派生出來,是一個執行級別的異常。意思是說可能會在執行的時候才會被丟擲,而且需要看這樣的執行級別異常是否會導致你的業務邏輯中斷。  2:空指標異常發生在物件為空,但是引用這個物件的方法。例如: String s =

java 必須用this的情況

摘抄自張孝祥老師的 java就業培訓教程。 this只存在與方法內部,用來代表呼叫改方法的物件。可以理解為每一個方法內部都有一個區域性變數叫this,每當初始化一個物件時,就把該物件的地址傳遞給了該物件每一個方法中的this變數,從而可以在方法內部使用這個的物件。 第一種情

js記憶體洩漏的情況

js記憶體洩露的幾種情況詳細探討 記憶體洩露是指一塊被分配的記憶體既不能使用,又不能回收,直到瀏覽器程序結束,由於瀏覽器垃圾回收方法有bug,會產生記憶體洩露,下面與大家詳細探討下記憶體洩露的幾種情況 記憶體洩露是指一塊被分配的記憶體既不能使用,又不

關於Java空指標異常的情況的總結(java.lang.NullPointerException)

在外文網站上看到的: 1. Calling the instance method of a null object.  2. Accessing or modifying the field of 

可能出現記憶體洩漏的情況

定義    簡單來說,記憶體洩漏就是程式在申請一個記憶體空間後沒有釋放,直到程式執行結束後才釋放。這樣看起來似乎沒什麼大問題,但是如果程式會持續執行很長時間(例如伺服器),並且可能在程每次呼叫某個部分的時候都會申請一個記憶體空間,那麼長久以來的後果是可想而知的:當程式

Android中記憶體洩漏的情況

1.單例造成的記憶體洩漏; Android中單例模式中的餓漢式寫法如下: public class Example  { private static Example Instance; private Example(Context context) { this.con

IDEA遇到Auto build completed with errors以及卡在parsing java...情況的分析

   在使用IDEA的時候,經常從類似git之類的地方將上面的程式碼clone並部署到本地。有時候配置完一些必要的東西之後,maven會根據專案配置好的pom.xml開始自動的下載依賴包,一般情況下會很順利的安裝好相關依賴的jar包然後直接點執行專案就完成部署了,具體步驟隨便

出現java.lang.IllegalStateException的情況

丟擲異常:ERROR [Engine] StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang.IllegalStateException: getOutputStream() has alr

Java 記憶體溢位java.lang.OutOfMemoryError)的常見情況和處理方式總結

http://outofmemory.cn/c/java-outOfMemoryError java.lang.OutOfMemoryError這個錯誤我相信大部分開發人員都有遇到過,產生該錯誤的原因大都出於以下原因:JVM記憶體過小、程式不嚴密,產生了過多的垃圾。

java中的對象(PO,VO,DAO,BO,POJO)

objects 其中 標準 setter ant object get 組件 工廠類 一、PO :(persistant object ),持久對象 可以看成是與數據庫中的表相映射的java對象。使用Hibernate來生成PO是不錯的選擇。二、VO :(value ob