文档章节

OutOfMemoryError异常的几种原因

爱吃西红柿的程序员
 爱吃西红柿的程序员
发布于 02/27 15:57
字数 1336
阅读 0
收藏 0

        在Java虚拟机规范中,除了程序计数器外,虚拟机内存的其他几个运行时区域都可能会发生OutOfMemoryError异常。

在IDEA中添加JVM参数如下:

         

 

一、Java堆溢出

        Java堆主要是用来存储对象,系统中不断的创建对象,并且在GC Roots到对象之间有可达路径,使垃圾回收机制不会回收这些对象,那么在对象数量达到最大堆的容量限制后就会产生内存溢出异常。代码如下:

/**
 * JVM参数:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\test\heapdump.hprof
 * -Xms:堆的最小内存
 * -Xmx:堆的最大内存
 * -XX:+HeapDumpOnOutOfMemoryError :出现内存溢出时Dump出当前内存堆转储快照
 * -XX:HeapDumpPath= :快照的存放路径
 */
public class HeapOOM {

    static class   OOMObject{}

    public static void main(String[] args) {
        List<OOMObject> list =  new ArrayList<OOMObject>();
        //不停的创建OOMObject
        while (true){
            list.add(new OOMObject());
        }
    }

}

    运行结果:

    堆内存溢出在实际应用中还是很常见的,出现堆内存溢出时,异常信息“java.lang.OutOfMemoryError”后会进一步提示 “Java heap space”。出现这个异常可以通过分析工具分析,确认时内存泄漏还是内存溢出。如果是泄露的话,继续使用工具分析具体泄露位置;不存在泄露,看看是不是可以加大堆内存容量。

二、虚拟机栈和本地方法栈溢出

    HotSpot虚拟机不区分虚拟机栈和本地方法栈,所以设置本地方法栈的参数-Xoss并没有效果;栈的容量只由-Xss参数设定。关于栈,Java虚拟机规范描述了两种异常:①如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。②如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。我们这里仅测试StackOverflowError异常如下:

/**
 * JVM参数:-Xss128k
 * 默认栈容量深度为:19029
 * 修改栈容量为128k深度为:1001
 * 通过减小栈的容量,可见栈的深度也变小。
 */
public class JavaVMStackSOF {

    private int stackLength = 1;

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

    public static void main(String[] args) throws Throwable {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try{
            oom.stackLeak();
        }catch (Throwable e){
            System.out.println("Stack Length :" + oom.stackLength);
            throw e;
        }
    }
}

    运行结果:(不同机器测试结果数量可能不同)

默认情况下:                                   -Xss128k:  

       虚拟机使用默认参数,栈深度在大多数情况下,完全够用了(包括递归)。

三、方法区(元数据区)溢出

        在JDK7之前方法区可以通过PermSize永久代大小和MaxPermSize最大永久代大小设置,如:-XX:PermSize=10m -XX:MaxPermSize=10m 。但是在JDK8中已经完全移除了永久代,PermSize和MaxPermSize参数也一并移除了。在移除了Perm区域之后,JDK8中使用MetaSpace来替代,这些空间都直接在堆上来进行分配。  在JDK8中,类的元数据存放在native堆中,这个空间被叫做:元数据区。JDK8中给元数据区添加了一些新的参数:

    ①-XX:MetaspaceSize=<NNN> :<NNN>是分配给类元数据区(以字节计)的初始大小(初始高水位),超过会导致垃圾收集器卸载类。这个数量是一个估计值。当第一次到达高水位的时候,下一个高水位是由垃圾收集器来管理的。

    ②-XX:MaxMetaspaceSize=<NNN> :<NNN>是分配给类元数据区的最大值(以字节计)。这个参数可以用来限制分配给类元数据区的大小。这个值也是个估计值。默认无上限。

    ③-XX:MinMetaspaceFreeRatio=<NNN>:<NNN>是一次GC以后,为了避免增加元数据区(高水位)的大小,空闲的类元数据区的容量的最小比例,不够就会导致垃圾回收。

    ④  -XX:MaxMetaspaceFreeRatio=<NNN>:<NNN>是一次GC以后,为了避免减少元数据区(高水位)的大小,空闲的类元数据区的容量的最大比例,超过就会导致垃圾回收。

    借助CGLib使元数据区出现内存溢出测试如下:

/**
 * JVM参数:-XX:MetaspaceSize=5M -XX:MaxMetaspaceSize=5M (JDK8)
 * -XX:PermSize=10m -XX:MaxPermSize=10m (JDK7)
 *  需要引入cglib依赖
 */
public class JavaMethodAreaOOM {

    public static void main(final 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 proxy) throws Throwable {
                    return proxy.invokeSuper(obj,args);
                }
            });
            enhancer.create();
        }
    }

    static class OOMObject{}

}

    运行结果:

        在很多主流框架中都是用到了CGLib,如Spring、Hibernate,需要增强的类越多,就需要越大的元数据区来保证动态生成的Class可以加载入内存。

四、本机直接内存溢出

        本机直接内存溢出(程序中直接或简介使用了NIO会导致),不像上面几种OutOfMemoryError会告诉我们溢出的位置,如下:

/**
 * JVM参数:-Xmx20m -XX:MaxDirectMemorySize=10m
 */
public class DircetMemoryOOM {

    private static final int _1MB = 1024*1024;

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

    运行结果:

        

 

 

源代码:https://gitee.com/itcaofanqi/CaoFanqiStudyRepository/tree/master/stujvm

参考:周志明《深入理解Java虚拟机》

 

© 著作权归作者所有

上一篇: JDK命令行工具
下一篇: JMeter入门学习
爱吃西红柿的程序员
粉丝 1
博文 70
码字总数 71191
作品 0
杭州
程序员
私信 提问
Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结

本来想写一写项目中遇到的内存溢出的问题,看了好多博客,发现有一篇特别好的博客,拾人牙慧,共享分享。 转载:http://outofmemory.cn/c/java-outOfMemoryError java.lang.OutOfMemoryErro...

mengdonghui123456
2017/02/07
0
0
异常、堆内存溢出、OOM的几种情况

1、堆内存溢出 【情况一】:   Java.lang.OutOfMemoryError: Java heap space:这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环;   如果是java堆内存不够的话,可...

www19
2017/06/14
0
0
如何修复Java中的VirtualMachineError

即使是最好的代码也会抛出错误,对开发人员而言这意味着需要知道如何修复它们。在这篇文章中,Ram Lakshmanan 介绍了许多不同类型的 VirtualMachineError 以及如何解决这些问题,在应用程序不...

java菜分享
01/28
17
0
java.lang.OutOfMemoryError异常解决方法

原因: 常见的有以下几种: 1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据; 2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收; 3.代码中存在死循环或循环产生过多...

白志华
2015/12/16
122
0
Java常见的几种内存溢出及解决方法

【情况一】: java.lang.OutOfMemoryError:Javaheapspace 【解释】:这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),另一个原因是程序中有死循环;如果是java堆内存不够的话...

小木头的冬天
2016/07/27
45
0

没有更多内容

加载失败,请刷新页面

加载更多

SSH安全加强两步走

从 OpenSSH 6.2 开始已经支持 SSH 多因素认证,本文就来讲讲如何在 OpenSSH 下启用该特性。 OpenSSH 6.2 以后的版本多了一个配置项 AuthenticationMethods。该配置项可以让 OpenSSH 同时指定...

xiangyunyan
27分钟前
4
0
C或C++不是C/C++

http://www.voidcn.com/article/p-mucdruqa-ws.html

shzwork
今天
6
0
OSChina 周六乱弹 —— 如何将梳子卖给和尚

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @for_ :划水五分钟,专注两小时。分享Various Artists的单曲《贝多芬第8号钢琴奏鸣曲悲伤的第三乐章》: 《贝多芬第8号钢琴奏鸣曲悲伤的第三乐...

小小编辑
今天
179
8
ES5

什么是ES5:比普通js运行要求更加严格的模式 为什么:js语言本身有很多广受诟病的缺陷 如何:在当前作用域的顶部添加:"use strict" 要求: 1、禁止给未声明的变量赋值 2、静默失败升级为错误...

wytao1995
今天
7
0
c++ 内联函数调用快的原因

见图片分析

天王盖地虎626
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部