java中内存溢出怎么解决,附几个内存溢出的例子!

java中内存溢出怎么解决,附几个内存溢出的例子!
在java虚拟机规范中,处理程序计数器之外,其他内存区域都有发生OutOfMemoryError(内存溢出)异常的可能。

一、java堆内存溢出
java堆用来存储对象实例,只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量之后就会产生内存溢出异常:

List<Object> list = new ArrayList<>();
                while (true) {
                    list.add(new Object());
                }
结果:Throwing OutOfMemoryError               

在java语言中,可以作为 GC Roots 的对象包括下面几种:
1、虚拟机栈(栈帧中本地变量表)中引用的对象
2、方法区中类静态属性引用的对象
3、方法区中常量引用的对象
4、本地方法栈中JNI(即一般所说的Native方法)引用的对象

解决:确认内存中对象存在是否必要,及检查是否有内存泄漏。
如果有内存泄漏,查看泄漏对象到 GC Roots 引用链,定位泄漏对象。
如果没有内存泄漏,检查虚拟机对参数(-Xmx和-Xms)是否可以调大,检查代码中是否有哪些对象的生命周期过长,尝试减少程序运行期的内存消耗

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

HotSpot虚拟机直接把本地方法栈和JAVA虚拟机栈和二为一了。对于HotSpot来说,虽然 -Xoss(设定本地方法栈大小)参数存在,但是无效的。栈大小只由 -Xss参数设定。

在Java虚拟机规范中描述了两种异常:
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError异常
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

在代码测试后发现:
单线程下,栈帧过大,虚拟机容量过小都不会导致OutOfMemoryError,只会导致StackOverflowError(栈会比内存先爆掉),一般多线程才会出现OutOfMemoryError,因为线程本身要占用内存。

例如:栈(StackOverflowError)
如果线程请求的栈深度大于虚拟机栈所允许的最大深度,将会抛出StackOverflowError异常:

private int stackLength = 1;
 
    public void stackLeak(){
        stackLength++;
        stackLeak();
    }
结果: java.lang.StackOverflowError: stack size 8MB  

方法执行时在虚拟机栈中会创建对应栈帧,不加限制的递归调用很快会撑爆虚拟机栈。

栈(OutOfMemoryError)
如果虚拟机在拓展栈时无法申请到足够的空间则抛出OutOfMemoryError异常

while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    dowhile();
                }
            });
            thread.start();
        }
private void dowhile() {
        while (true) {
        }
    }

注:上述代码执行有风险,可能会导致系统假死

解决:
如果是多线程导致的OutOfMemoryError,在不能减少线程数或更换64位虚拟机的情况,只能通过减少最大堆和减少栈容量来换取更多的线程;(这个调节思路和 Java 堆出现 OOM 正好相反,Java 堆出现 OOM 要调大堆内存的设置值,而栈出现 OOM 反而要调小)

三、方法区和运行时常量池溢出

首先介绍String.intern()这个本地方法,它的作用是:如果字符串常量池中已经包含一个此String对象的字符串,则返回代表池中这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。

在 JDK1.6 我们可通过-XX: PermSize 和 -XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量

常量池内存溢出:

//使用List保持着常量池引用,避免Full GC回收常量池行为
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }

这段代码在JDK1.6时运行会抛出OutOfMemoryError异常,在 JDK1.7时则不会,程序会一直while循环下去。

方法区内存溢出:思路,动态创建Class对象。大量产生JSP或动态产生JSP文件的应用,JSP第一次运行时会被编译成Class类。

四、本地直接内存溢出

DirectMemory容量可通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与java堆最大值一样,虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是unsafe.allocateMemory()

private final int_1MB = 1024 * 1024;

Field unsafeField = Unsafe.class.getDeclaredFields()[0];
		unsafeField.setAccessible(true);
		Unsafe unsafe = (Unsafe) unsafeField.get(null);
		while(true){
			unsafe.allocateMemory(_1MB);
		}
结果:java.lang.OutOfMemoryError
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值