论JVM爆炸的几种姿式及自救方法

前言

现在不论是在面试仍是在咱们的工做中,OOM老是不断的出如今咱们的视野中,因此咱们有必要去了解一下致使OOM的缘由以及一些基本的调整方法,你们能够经过下面的事例来了解一下什么样的代码会致使OOM,帮助咱们之后在工做中可以经过异常信息来判断是JVM里面哪一个区域出现了问题。java

先介绍一下笔者的相关编码环境。面试

jdk:java version "1.8.0_121"bash

ide:IntelliJ IDEA 2019.1 (Community Edition)jvm


正文

1.Java堆溢出

Java中的堆存储的都是对象实例,当咱们不断的建立对象,而GC的时候又不能回收,当存储的对象大小超过了-Xmx的值,这时候则会出现OutOfMemoryError.[-XX:+HeapDumpOnOutOfMemoryError]参数可让jvm出现内存溢出的时候dump出内存堆转储快照。ide

/**
 * VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
 * @author wangzenghuang
 */
public class HeapOOMDemo {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        while(true){
            stringList.add("str");
        }
    }
}

复制代码

运行结果,发生OOM,而且在咱们项目的根目录dump出当前的内存堆快照工具

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1376.hprof ...
Heap dump file created [7972183 bytes in 0.047 secs]
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 HeapOOMDemo.main(HeapOOMDemo.java:12)

Process finished with exit code 1
复制代码

简单解决思路优化

那么发生这个问题之后咱们的解决思路有哪些呢?咱们能够利用一些工具(例如Eclipse Memory Analyzer )来分析dump出的文件,通常来讲,当生产环境发生OOM,比较常见的一个缘由是发生了内存泄漏,用工具能够分析出泄露的对象到GC Root的引用链,从而定位到问题代码。假如通过分析后发现内存中的对象都是“必须存活”的对象,这时候就要思考下项目中是否把“-Xms跟-Xmx”设置得过小了(固然这里也不是随意调大,须要结合机器的物理内存状况),再者须要留意代码中是否有一些长生命周期的对象,从代码中优化内存消耗。ui

2.方法区溢出

在jvm的方法区中,它主要存放了类的信息,常量,静态变量等。在jdk8之前是经过“-XX:PermSize,-XX:MaxPermSize”来调整这个区域的值,可是从8开始呢,永久代的概念被MetaSpace(元空间)代替了,对应的参数也变成了“-XX:MetaspaceSize,-XX:MaxMetaspaceSize”。在这个例子中使用CGLib来动态生成一些类,方便咱们实验操做。编码

/**
 * VM Args: -XX:MetaspaceSize=5m -XX:MaxMetaspaceSize=5m
 * @author wangzenghuang
 */
public class MethodAreaOOMDemo {
    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[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(obj,objects);
                }
            });
            enhancer.create();
        }
    }
    static class OOMObject{}
}
复制代码

运行结果idea

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
复制代码

简单解决方法

这个问题的话,通常来讲根据状况调整方法区的大小就好了,网上也有人说能够去掉MetaSpace的的大小限制,可是不建议这么干,毕竟不可控的事情咱们要少点干,很容易给本身埋雷。

3.栈溢出

对于咱们来讲,还有一个熟悉的错误,那就是“StackOverflowError”,它是由线程请求的栈深度超过了jvm容许的最大范围而产生的。“-Xss”参数能够设置栈容量。

/**
 * VM Args: -Xss128k
 * @author wangzenghuang
 */
public class StackOFDemo {
    private static int stackLength = 1;

    public void stackLeak(){
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        StackOFDemo stackOFDemo = new StackOFDemo();
        try {
            stackOFDemo.stackLeak();
        }catch (Throwable e){
            System.out.println("length : "+ stackLength);
            throw e;
        }
    }
}
复制代码

运行结果

length : 983
Exception in thread "main" java.lang.StackOverflowError
	at StackOFDemo.stackLeak(StackOFDemo.java:10)
	at StackOFDemo.stackLeak(StackOFDemo.java:10)
    ...
复制代码

简单解决思路

通常来讲此类问题多出如今存在递归的地方,要从代码里从新审视递归未结束的缘由,若递归的方法没问题能够根据实际状况调整“-Xss”参数的大小。还有一些代码的循坏依赖也会形成此类状况,

4.直接内存溢出

本机直接内存默认与“-Xmx”设定的值同样大,能够经过“-XX:MaxDirectMemorySize”修改。

/**
 * VM Args: -Xmx20m -XX:MaxDirectMemorySize=10
 * @author wangzenghuang
 */
public class DirectMemoryOOMDemo {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws IllegalAccessException {
        Field field = Unsafe.class.getDeclaredFields()[0];
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        while (true){
            unsafe.allocateMemory(_1MB);
        }
    }
}

复制代码

运行结果

呃,一运行这段代码idea直接闪退了,查阅其余资料能够得知当DirectMemory致使内存溢出时,Heap Dump文件是很小的,若是程序中有使用NIO的状况能够检查一下。

总结

这里所展现的代码只是能够触发jvm的各类错误,可是并不表明这是惟一的触发错误的方方式,假如咱们的代码比较复杂,有时候遇到相似错误的时候仍是须要耐心分析。

喜欢的话,关注一下公众号《深夜里的程序猿》吧,天天分享最干的干货
相关文章
相关标签/搜索