Skip to content

Java OOM归类 #6

@chaojun-zhang

Description

@chaojun-zhang

Java OOM归类

除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能

内存模型

image

  • Error 1- Java heap space
// Java program to illustrate 
// Heap error 
import java.util.*; 

public class Heap { 
    static List<String> list = new ArrayList<String>(); 
    public static void main(String args[]) throws Exception { 
        Integer[] array = new Integer[10000 * 10000]; 
    } 
} 

解决方案
- 检查是否存在大对象分配,或者大数组分配
- jmap dump下来在使用mat工具分析看是否有内存泄漏
- 加大-Xmx内存
- 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性
-

  • Error 2 GC Overhead limit exceeded
    这个主要说明了JVM的GC一直在运行,并且程序已经运行很慢。通常98%的时间在做GC,那么这个时候会抛出这个异常
// Java program to illustrate 
// GC Overhead limit exceeded 
import java.util.*; 
  
public class Wrapper { 
public static void main(String args[]) throws Exception 
    { 
        Map m = new HashMap(); 
        m = System.getProperties(); 
        Random r = new Random(); 
        while (true) { 
            m.put(r.nextInt(), "randomValue"); 
        } 
    } 
} 

如果你用java -Xmx100m -XX:+UseParallelGC 来运行上面的代码,那么这个错误就会抛出

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.valueOf(Integer.java:832)
at Wrapper.main(error.java:9)

Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。

解决方法
1、检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。
2、添加参数 -XX:-UseGCOverheadLimit 禁用这个检查,其实这个参数解决不了内存问题,只是把错误的信息延后,最终出现 java.lang.OutOfMemoryError: Java heap space。
3、dump内存,检查是否存在内存泄露,如果没有,加大内存。

  • Error 3 - Permgen space is thrown
    java内存分为不同的区域,包括permgen area都是在JVM启动的时候指定的,这个通常是说方法区的内存太小导致的。比如加载的class超过了这个区域指定的大小
// Java program to illustrate 
// Permgen Space error 
import javassist.ClassPool; 
  
public class Permgen { 
    static ClassPool classPool = ClassPool.getDefault(); 
  
public static void main(String args[]) throws Exception 
    { 
        for (int i = 0; i < 1000000000; i++) { 
            Class c = classPool.makeClass(com.saket.demo.Permgen" + i).toClass(); 
            System.out.println(c.getName()); 
        } 
    } 
} 

可以通过修改以下参数调整

java -XX:MaxPermSize=512m com.saket.demo.Permgen(这个已经在JAVA8 中废弃了)

  • Error 4 - Metaspace
    java 类元数据是分配在native 内存中的,如果这个部分的内存超出,也会抛出OOM。
// Java program to illustrate 
// Metaspace error 
import java.util.*; 
  
public class Metaspace { 
    static javassist.ClassPool cp = javassist.ClassPool.getDefault(); 
  
public static void main(String args[]) throws Exception 
    { 
        for (int i = 0; i < 100000; i++) { 
            Class c = cp.makeClass("com.saket.demo.Metaspace" + i).toClass(); 
        } 
    } 
} 

这段代码持续生成新类并且加载到元数据空间中,直到完全用完元数据空间,然后就会抛出java.lang.OutOfMemoryError: Metaspace。

解决方法
因为该OOM原因比较简单,解决方法有如下几种:
1、检查是否永久代空间或者元空间设置的过小 ( -XX:MaxMetaspaceSize)
2、检查代码中是否存在大量的反射操作
3、dump之后通过mat检查是否存在大量由于反射生成的代理类
4、放大招,重启JVM

  • Error 5 – Requested array size exceeds VM limit :
    主要表示应用尝试分配一个数组大于堆的大小,就会报出这个错误,举个栗子,比如在512的内存对空间中,尝试分配一个1024M的数组就会抛出这样的错误

  • Error 6 – Request size bytes for reason. Out of swap space?
    java.lang.OutOfMemoryError: Out of swap space 通常是由以下的原因造成的:

    • The operating system is configured with insufficient swap space.(操作系统的swap space不足)
    • Another process on the system is consuming all memory resources.(别的进程占用所有的内存资源)
  • Error 7 : reason stack_trace_with_native_method

// Java program to illustrate 
// new native thread error 
import java.util.*; 
  
public class GFG { 
public static void main(String args[]) throws Exception 
    { 
        while (true) { 
            new Thread(new Runnable() 
            { 
                public void run() 
                { 
                    try
                    { 
                        Thread.sleep(1000000000); 
        } 
        catch (InterruptedException e) 
        { 
        } 
    } 
            }).start(); 
   } 
  } 
} 
  • Error 8 Map failed
  • Caused by: java.lang.OutOfMemoryError: Map failed
    这个一般是由于程序使用了FileChannelImpl这个类导致的,里面用到了底层的内存映射文件,当map file个数增长,系统的启动参数上加上关闭手动GC导致的(-XX:+DisableExplicitGC),导致map file个数达到了操作系统的max_map_count后直接OOM了。
    Java的FilechannelImpl在使用时,如果发现map第一次出现OOM时,会调用System.gc去回收,但是我们又在启动参数上关闭了系统System.gc的能力。最后导致map失败。
    解决方法:
    -XX:+DisableExplicitGC这个参数不能随便关闭,原有是有些系统或者框架使用了DirectByteBuffer和FileChannel.map。这些占用的内存达到了最大临界值的时候,需要依赖调用System.gc来强制释放。

如果这个参数关闭了,那么内存释放就无效了,看到在下面第一次出现OutofMemoryError的时候,会调用System.gc(),然后等待100ms后,再次进行map操作,如果还是失败了,那就会抛出Map failed 。

	    try {
           var7 = this.map0(var6, var13, var15);
        } catch (OutOfMemoryError var30) {
            System.gc();

            try {
                Thread.sleep(100L);
            } catch (InterruptedException var29) {
                Thread.currentThread().interrupt();
            }

            try {
                var7 = this.map0(var6, var13, var15);
            } catch (OutOfMemoryError var28) {
                throw new IOException("Map failed", var28);
            }
        } 

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions