深入理解java虚拟机专题—实战 OutOfMemoryError 异常

前言:

通过这篇博文,我们可以学习到:第一,通过代码验证Java 虚拟机规范中描述的各个运行时区域存储的内容;第二,希望我们在工作中遇到实际的内存溢出异常时,能根据异常的信息快速判断是哪个区域的内存溢出,知道什么样的代码可能会导致这些区域内存溢出,以及出现这些异常后该如何处理。

Tips:

下文代码的开头都注释了执行时所需要设置的虚拟机启动参数(注释中“VM Args” 后面跟着的参数),这些参数对实验的结果有直接的影响,读者调试代码的时候千万不要忽略,如果读者使用控制台命令来执行程序,那直接跟在java命令之后书写就可以。如果读者使用 IntelliJ,可参考下图配置:

Java 堆溢出:

代码清单如下:

package com.alipay.functest.aaa_test;



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

/**
 * @ClassName HeapOOM
 * @Description 通过设定不同的 VM arguments来测试各个运行时区域的内存溢出异常
 * VM Args: -Xms20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError
 * @Author 文竹
 * @Date 2019/8/22 14:50
 * @Version 1.0
 **/

public class HeapOOM{
    static class OOMObject{}

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();

        while (true){
            list.add(new OOMObject());
        }
    }
}

执行结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid120800.hprof ...
Heap dump file created [2314735266 bytes in 16.550 secs]
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:67)
Caused by: 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:265)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
	at java.util.ArrayList.add(ArrayList.java:462)
	at com.alipay.functest.aaa_test.HeapOOM.main(HeapOOM.java:24)
	... 5 more

Process finished with exit code 1

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

要解决这个区域的异常,一般的手段是先通过内存映像分析工具(如 Eclipse Memory Analyzer)对 Dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow).

堆转储文件:

è¿éåå¾çæè¿°

如果内存泄漏,可进一步通过工具查看泄漏对象到GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots 相关联并导致垃圾收集器无法自动回收他们的。掌握了泄漏对象的类型信息及GC Roots 引用链的信息,就可以比较准确的定位出泄漏代码的位置。

如果不存在泄漏,换句话说,就是内存中的对象却是都还必须存活着,那就应该检查虚拟机的堆参数(-Xmx 与 -Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否在某些对象生命周期过长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

https://blog.csdn.net/u013066244/article/details/82356260(使用 Eclipse Memory Analyzer 进行堆转储文件分析)

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

由于在HotSpot 虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot 来说,虽然 -Xoss参数(设置本地方法栈大小)存在,但实际上是无效对的,栈容量只由 -Xss 参数设定。关于虚拟机栈和本地方法栈,在java虚拟机规范中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将跑出 StackOverflowError 异常。
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则跑出OutOfMemoryError 异常。

这里把异常分成两种情况,看似更加严谨,但却存在着一些相互重叠的地方:当栈空间无法继续分配时,到底是内存太小,还是已使用的栈空间太大,其本质上只是对同一件事情的两种描述而已。

限于篇幅太长,这里就不展开了,可参考《深入理解 Java 虚拟机》第二版, 第二章 Java 内存区域与内存溢出异常,2.4.2  虚拟机栈和本地方法栈溢出相关内容。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值