【OOM】Java虚拟机常见内存溢出错误

1、引言

从事java开发的小伙伴在平时的开发工做中,应该会碰见各式各样的异常和错误,在实际工做中积累的异常或者错误越多,趟过的坑越多,就会使咱们编码更加的健壮,就会本能地避开不少严重的坑。如下介绍几个Java虚拟机常见内存溢出错误。以此警示,避免生产血案。java

2、模拟Java虚拟机常见内存溢出错误

一、内存溢出之栈溢出错误

package com.jayway.oom;  
  
/**  
 * 栈溢出错误  
 * 虚拟机参数:-Xms10m -Xmx10m  
 * 抛出异常:Exception in thread "main" java.lang.StackOverflowError  
 */
 public class StackOverflowErrorDemo {  
  
 public static void main(String[] args) {  
    stackOverflowError();  
 }  
  
 private static void stackOverflowError() {  
    stackOverflowError();  
 }  
  
}

二、内存溢出之堆溢出错误

package com.jayway.oom;  
  
import java.util.Random;  
  
/**  
 * 堆溢出错误  
 * 虚拟机参数:-Xmx10m -Xms10m
 * 抛出异常:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  
 */
 public class JavaHeapSpaceErrorDemo {  
  
 public static void main(String[] args) {  
    String temp = "java";  
    //不断地在堆中开辟空间,建立对象,撑爆堆内存
    while (true) {  
        temp += temp + new Random().nextInt(111111111) + new Random().nextInt(222222222);  
        temp.intern();  
    }  
 }  
}

三、内存溢出之GC超过执行限制错误

package com.jayway.oom;  
  
import java.util.ArrayList;  
import java.util.List;  
  
/**  
 * GC超过执行限制错误  
 * 虚拟机参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m  
 * * 抛出异常:Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded  
 * * 致使缘由:GC回收时间过长会抛出OutOfMemoryError,何为过长,即超过98%的cpu时间用来作GC垃圾回收  
 * 可是回收效果甚微,仅仅只有2%的CPU时间用来用户程序的工做,这种状态是很糟糕的,程序在不断地GC  
 * 造成恶性循环,CPU的使用率一直是满负荷的,正经活却没有干,这种状况虚拟机只好抛出错误来终止程序的执行  
 *  
 * 不断地Full GC,事倍功微  
 * [Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7167K->7161K(7168K)] 9215K->9209K(9728K), [Metaspace: 3529K->3529K(1056768K)], 0.0291829 secs] [Times: user=0.08 sys=0.02, real=0.03 secs]  
 */
 public class GCOverheadErrorDemo {  
  
    public static void main(String[] args) {  
        int i = 0;  
        List<String> list = new ArrayList<>();  
        try {  
            while (true) {  
            list.add(String.valueOf(++i).intern());  
            }  
        } catch (Throwable e) {  
            System.out.println("*****************i:" + i);  
            e.printStackTrace();  
            throw e;  
        }  
    }  
}

四、内存溢出之直接内存溢出错误

package com.jayway.oom;  
  
import java.nio.ByteBuffer;  
  
/**  
 * 直接内存溢出错误  
 * 抛出异常:Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory  
 * * 配置虚拟机参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m  
 * * 致使缘由:一般NIO程序常用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的IO方式,  
 * 它可使用Native函数库直接分配堆外内存,而后经过一个存储在java堆里面的DirectByteBuffer对象做为这块内存的引用,  
 * 这样能子一些场景中显著提升性能,由于避免了在Java堆和Native内存中来回复制数据。  
 *  
 * ByteBuffer.allocate(capability):分配JVM堆内存,数据GC的管辖范围,因为须要拷贝因此速度相对较慢  
 *  
 * ByteBuffer.allocate(capability):分配OS本地内存,不属于GC管辖范围,因为不须要内存拷贝,因此速度相对较快。  
 *  
 * 可是若是不断分配本地内存,堆内存不多使用,那么JVM就不须要执行GC,DirectByteBuffer对象就不会被回收,此时若是继续分配堆外内存,  
 * 可能堆外内存已经被耗光了没法继续分配,此时程序就会抛出OutOfMemoryError,直接崩溃。  
 *  
 */
 public class DirectBufferMemoryErrorDemo {  
  
    public static void main(String[] args) {  
        //默认JVM配置的最大直接内存是总物理内存的四分之一  
        long maxDirectMemory = sun.misc.VM.maxDirectMemory() / 1024 / 1024;  
        System.out.println("配置的maxDirectMemory:" + maxDirectMemory + "MB");  
  
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);  
    }  
  
}

五、内存溢出之没法建立新的本地线程

package com.jayway.oom;  
  
/**  
 * 内存溢出之没法建立新的本地线程  
 * 抛出异常:java.lang.OutOfMemoryError: unable to create new native thread  
 * * 描述:  
 * 高并发请求服务器时,常常出现java.lang.OutOfMemoryError: unable to create new native thread  
 *      native thread异常与对应的平台有关  
 *  
 * 致使缘由:  
 *      一、应用程序建立了太多线程了,一个应用进程建立的线程数超过系统承载极限。  
 *      二、操做系统并不容许你的应用进程建立这么多的线程,linux系统默认容许单个进程能够建立的线程数是1024个  
 *  
 * 解决方法:  
 *      一、想办法下降应用进程建立的线程数量,  
 *      二、若是应用程序确实须要这么多线程,超过了linux系统的默认1024个限制,能够经过修改linux服务器配置,提升这个阈值。  
 *  
 */
 public class UnableCreateNativeThreadErrorDemo {  
  
    public static void main(String[] args) {  
        for (int i = 0; true; i++) {  
            System.out.println("***************i:" + i);  
  
            //不断得建立新线程,直到超过操做系统容许应用进程建立线程的极限  
            new Thread(() -> {  
                try {  
                    Thread.sleep(Integer.MAX_VALUE);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }).start();  
        }  
    }  
}

六、内存溢出之元空间溢出错误

package com.jayway.oom;  
  
import org.springframework.cglib.proxy.Enhancer;  
import org.springframework.cglib.proxy.MethodInterceptor;  
import org.springframework.cglib.proxy.MethodProxy;  
  
import java.lang.reflect.Method;  
  
/**  
 * 元空间溢出错误  
 * 抛出异常:java.lang.OutOfMemoryError: Metaspace  
 * * 设置虚拟机参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m  
 * * 描述:Java8及之后的版本使用Metaspace来替代了永久代。metaspace是方法区在HotSpot中的实现,它与持久代最大的区别在于  
 *      Metaspace并不在虚拟机内存中而是在本地内存中。  
 *  
 * 元空间存储了如下信息:  
 *      一、虚拟机加载的类信息  
 *      二、常量池  
 *      三、静态变量  
 *      四、即时编译后的代码  
 *  
 */
 public class MetaspaceErrorDemo {  
  
    static class OOMTest {  
    }  
  
    public static void main(String[] args) {  
        int count = 0;  
  
        try {  
        //cglib不断建立类,模拟Metaspace空间溢出,咱们不断生成类往元空间中灌,超过元空间大小后就会抛出元空间移除的错误  
            while (true) {  
                count++;  
                Enhancer enhancer = new Enhancer();  
                enhancer.setSuperclass(OOMTest.class);  
                enhancer.setUseCache(false);  
                enhancer.setCallback(new MethodInterceptor() {  
                    @Override  
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
                        return methodProxy.invokeSuper(o, args);  
                    }  
                });  
                enhancer.create();  
            }  
        } catch (Throwable e) {  
            System.out.println("************多少次后发生了异常:" + count);  
            e.printStackTrace();  
        }  
    }  
}
相关文章
相关标签/搜索