Java内存溢出异常

Java堆溢出

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

package com.xrq.test;

import java.util.ArrayList;
import java.util.List;

/**
 * 测试内容:堆溢出
 *
 * 虚拟机参数:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOverflowTest
{
    public static void main(String[] args)
    {
        List<HeapOverflowTest> list = new ArrayList<HeapOverflowTest>();
        while (true)
        {
            list.add(new HeapOverflowTest());
        }
    }
}
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid8876.hprof ...
Heap dump file created [15782068 bytes in 0.217 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2760)
    at java.util.Arrays.copyOf(Arrays.java:2734)
    at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
    at java.util.ArrayList.add(ArrayList.java:351)
    at com.xrq.test.HeapOverflowTest.main(HeapOverflowTest.java:18)

-Java堆内存的OOM异常是实际应用中常见的内存溢出异常情况。当出现Java堆内存溢出时,异常堆中信息"java.lang.OutOfMemoryError"会跟着进一步提示‘“Java heap space”。

解决方案:一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer) 对Dump出来的堆转储快照分析,重点是确认内存中的对象是否是必要,也就是先分清楚是内存泄漏还是内存溢出引起的OOM。

内存泄漏(不再会被使用的对象内存不能被GC):进一步使用工具查看泄漏对象到GC Roots的引用链。掌握了泄漏对象的类型信息及GC Roots引用链的信息,就可以比较准确的定位出泄漏代码的位置。

内存溢出:检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

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

HotSpot虚拟机中不区分虚拟机栈和本地方法栈,因此栈容量只有-Xss参数设置。关于虚拟机栈和本地方法栈,在虚拟机规范中描述了两种异常:

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

(2)如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

package com.xrq.test;

/**
 * 测试内容:栈溢出测试(递归调用导致栈深度不断增加)
 * 
 * 虚拟机参数:-Xss128k
 */
public class StackOverflowTest
{
    private int stackLength = 1;
    
    public void stackLeak()
    {
        stackLength++;
        stackLeak();
    }
    
    public static void main(String[] args) throws Throwable
    {
        StackOverflowTest stackOverflow = new StackOverflowTest();
        try
        {
            stackOverflow.stackLeak();
        }
        catch (Throwable e)
        {
            System.out.println("stack length:" + stackOverflow.stackLength);
            throw e;
        }        
    }
}

 

stack length:1006
Exception in thread "main" java.lang.StackOverflowError
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:14)
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
  ...

StackOverFlowError这个异常,有错误堆栈可以阅读,比较好定位。而且如果使用虚拟机默认参数,栈深度在大多数情况下,达到1000~2000完全没有问题,正常方法的调用这个深度应该是完全够了。但是如果建立过多线程导致的OutOfMemoryError,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减小最大堆容量和减小栈容量来换取更多的线程了。

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

运行时常量池也是方法区的一部分,所以这两个区域一起看就可以了。这个区域的OutOfMemoryError可以利用String.intern()方法来产生。这是一个Native方法,意思是如果常量池中有一个String对象的字符串就返回池中的这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中去,并且返回此String对象的引用。

package com.xrq.test;

import java.util.ArrayList;
import java.util.List;

/**
 * 测试内容:常量池溢出(这个例子也可以说明运行时常量池为方法区的一部分)
 * 
 * 虚拟机参数-XX:PermSize=10M -XX:MaxPermSize=10M
 */
public class ConstantPoolOverflowTest
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true)
        {
            list.add(String.valueOf(i++).intern());
        }
    }
}

 

Exception in thread "Reference Handler" Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at com.xrq.test.ConstantPoolOverflowTest.main(ConstantPoolOverflowTest.java:19)
java.lang.OutOfMemoryError: PermGen space
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:123)

对于HotSpot而言,方法区=永久代,这里看到OutOfMemoryError的区域是“PermGen space”,即永久代,那其实也就是方法区溢出了。注意一下JDK1.7下是不会有这个异常的,while循环将一直下去,因为JDK1.7之后溢出了永久代并采用Native Memory来实现方法区的规划了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值