項目總結65:內存溢出OOM問題處理
OOM,即OutOfMemory,內存溢出,原因是:分配的太少;用的太多;用完沒釋放。理論上,JVM中除了程序計數器,堆內存,方法區,虛擬機方法棧,本地方法棧,都會出現OOM問題
常見的OOM情況有三種:
1- java.lang.OutOfMemoryError: Java heap space ------>java堆內存溢出,此種情況最常見,一般由於內存泄露或者堆的大小設置不當引起。對於內存泄露,需要通過內存監控軟件查找程序中的泄露代碼,而堆大小可以通過虛擬機參數-Xms,-Xmx等修改。
2- java.lang.OutOfMemoryError: PermGen space/ Metaspace------>java永久代(元數據)溢出,即方法區溢出了,一般出現於大量Class或者jsp頁面,或者采用cglib等反射機制的情況,因為上述情況會產生大量的Class信息存儲於方法區。此種情況可以通過更改方法區的大小來解決,使用類似-XX:PermSize/MetaspaceSize=64m -XX:MaxPermSize/MaxMetaspaceSize =256m的形式修改。另外,過多的常量也會導致方法區溢出。
3- java.lang.StackOverflowError ------>不會拋OOM error,但也是比較常見的Java內存溢出。JAVA虛擬機棧溢出,一般是由於程序中存在死循環或者深度遞歸調用造成的,棧大小設置太小也會出現此種溢出。可以通過虛擬機參數-Xss來設置棧的大小。
問題處理
一、 OOM: Java heap space;
問題示例:
public static void main(String[] args) { //1- 堆內存溢出 List<OOMObject> list = new ArrayList<>(); int i = 0; while (true){ list.add(new OOMObject()); System.out.println(++i); } } 日志: java.lang.OutOfMemoryError: Java heap space Dumping heap to D:/WORK/JVM\java_pid22096.hprof ... Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at jvm.OOMTest.main(OOMTest.java:21)
解決方案:
1- 設置虛擬機運行參數,打印日志
-server -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/WORK/JVM
其中 -Xms20m -Xmx20m,是故意減少對內存,暴露問題;
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/WORK/JVM;將OOM異常體脂打印到D:/WORK/JVM文件夾下,生成的文件如下:
2- 使用jprofile軟件打開hprof文件,如下圖,分析問題原因;發現OOMObject對象被創建了81W次,導致內存溢出;
3- 對症下葯
三、 StackOverflowError 異常
示例
public static void main(String[] args) { //2- 棧溢出 recursion(0); } static int recursion(int num){ if(num == 0){ System.out.println(num); return recursion(num); } return 0; } } 日志如下: Exception in thread "main" java.lang.StackOverflowError at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691) at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.PrintStream.write(PrintStream.java:526) at java.io.PrintStream.print(PrintStream.java:597) at java.io.PrintStream.println(PrintStream.java:736) at jvm.OOMTest.recursion(OOMTest.java:31) at jvm.OOMTest.recursion(OOMTest.java:32)
解決方案:根據程序的.out日志文件(比如tomcat的catalina.out文件),排查問題出現在在哪里
END