Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java OOM归类 #6

Open
chaojun-zhang opened this issue Nov 25, 2019 · 0 comments
Open

Java OOM归类 #6

chaojun-zhang opened this issue Nov 25, 2019 · 0 comments

Comments

@chaojun-zhang
Copy link
Owner

chaojun-zhang commented Nov 25, 2019

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);
            }
        } 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant