Java内存溢出(OOM)分析

时间:2022-09-26 13:59:15
当JVM内存不足时,会抛出java.lang.OutOfMemoryError.
 
主要的OOM类型右:
  • Java heap space:堆空间不足
  • GC overhead limit exceeded : GC开销超出限制
  • Permgen space:永久代内存不足
  • Metaspace:元空间内存不足
  • Unable to create new native thread:无法创建新的本地线程
  • Out of swap space? : 交换空间内存不足
  • Kill process or sacrifice child

 

Java heap space:堆空间不足

  • 通用解决方案:通过-Xmx设置更大的堆内存【该方式可能只是延迟报错的发生,如果不能从根本上找到原因,报错还是可能会发生】
  • 进一步原因分析及解决方案:
    • 流量/数据量峰值 : 可以考虑添加机器资源,或者做限流
    • 内存泄漏 : 需要找到持有的对象,修改代码
    • 创建了一个超大对象(通常是一个大数组) : 可以进行业务切分
  • 代码示例
    • 内存泄漏【-Xmx10m】
package oom;

import java.util.HashMap;
import java.util.Map;

/**
* 内存泄露
*/
public class JavaHeapSpace2 {

    public static void main(String[] args) {
        Map<Key,String> map = new HashMap<>();
        while (true) {
            Key key = new Key();
            if(!map.containsKey(key)) {
                map.put(key, "Java Overhead");
                System.out.println(key);
            }
        }

    }
}

class Key { }
View Code
oom.Key@2ef70cb4
......
oom.Key@457298d0
oom.Key@484b94f2
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:704)
    at java.util.HashMap.putVal(HashMap.java:663)
    at java.util.HashMap.put(HashMap.java:612)
    at oom.JavaHeapSpace2.main(JavaHeapSpace2.java:16)
View Result
    • 创建了一个超大对象
package oom;

import java.lang.management.ManagementFactory;
import java.util.List;

public class JavaHeapSpace {

    private static final int SIZE = 12 * 1024 * 2014;

    public static void main(String[] args) {
        List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
        System.out.println("JVM Arguments : " + inputArguments);
        int[] arr = new int[SIZE];
    }
}
View Code
JVM Arguments : [-Xmx12m, -Dfile.encoding=GBK]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at oom.JavaHeapSpace.main(JavaHeapSpace.java:13)

Process finished with exit code 1
View Result[-Xmx12m]
JVM Arguments : [-Xmx35m,-Dfile.encoding=GBK]

Process finished with exit code 0
View Result[-Xmx35m]

 

GC overhead limit exceeded : GC开销超出限制

默认情况下,当应用程序花费超过98%的时间用来做GC并且回收了不到2%的堆内存时,会抛出java.lang.OutOfMemoryError:GC overhead limit exceeded错误。
此类问题的原因与解决方案跟 Java heap space 非常类似,可以参考上文
package oom;

import java.util.HashMap;
import java.util.Map;

public class JavaHeapSpace2 {

    public static void main(String[] args) {
        Map<Key,String> map = new HashMap<>();
        while (true) {
            Key key = new Key();
            if(!map.containsKey(key)) {
                map.put(key, "Java Overhead");
                System.out.println(key);
            }
        }
    }
}

class Key { }
View Code
oom.Key@61f7f66c
oom.Key@1da844d
......
oom.Key@792b37e7
oom.Key@3d8151c0Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.nio.CharBuffer.wrap(CharBuffer.java:373)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
View Result

 

Permgen space:永久代内存不足

 

Metaspace:元空间内存不足
package oom;

import javassist.CannotCompileException;
import javassist.ClassPool;

import java.lang.management.ManagementFactory;
import java.util.List;

public class Metaspace{

    public static void main(String[] args) throws CannotCompileException {
        List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
        System.out.println("JVM Arguments : " + inputArguments);

        for (int i = 0; i < 100000000; i++) {
            ClassPool.getDefault().makeClass("User" + i).toClass();
        }
    }
}
View Code
JVM Arguments : [-XX:MaxMetaspaceSize=35m, -Dfile.encoding=GBK]
Exception in thread "main" javassist.CannotCompileException: by java.lang.OutOfMemoryError: Metaspace
    at javassist.ClassPool.toClass(ClassPool.java:1099)
    at javassist.ClassPool.toClass(ClassPool.java:1042)
    at javassist.ClassPool.toClass(ClassPool.java:1000)
    at javassist.CtClass.toClass(CtClass.java:1224)
    at oom.Permgen.main(Permgen.java:16)
Caused by: java.lang.OutOfMemoryError: Metaspace
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at javassist.ClassPool.toClass2(ClassPool.java:1112)
    at javassist.ClassPool.toClass(ClassPool.java:1093)
    ... 4 more
View Result

 

Unable to create new native thread:无法创建新的本地线程
package oom;

public class UnableCreateThread {

    public static void main(String[] args) {
        while (true) {
            new Thread(() -> {
                try {
                    Thread.sleep(100000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
View Code
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006a674447, pid=18648, tid=0x0000000000067b68
#
# JRE version: Java(TM) SE Runtime Environment (8.0_171-b11) (build 1.8.0_171-b11)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# V  [jvm.dll+0x214447][thread 424944 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424964 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424984 also had an error]
[thread 424992 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424988 also had an error]
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:717)
    at oom.UnableCreateThread.main(UnableCreateThread.java:14)
View Result

 

Out of swap space? : 交换空间内存不足
 
Kill process or sacrifice child
 
你可以通过修改各种启动参数来“快速修复”这些内存溢出错误,但你需要正确区分你是否只是推迟或者隐藏了java.lang.OutOfMemoryError的症状。如果你的应用程序确实存在内存泄漏或者本来就加载了一些不合理的类,那么所有这些配置都只是推迟问题出现的时间而已,实际也不会改善任何东西。
 
 
参考文献:

 

标签:

相关文章