实战Java虚拟机OOM - Java堆内存溢出

阅读:176

1.内存溢出实例

之前章节讲过Java运行时数据区内存划分,除了程序计数器之外,其它区域都会发生OutOfMemoryError这异常。本节针对堆异常做出讲解。

我们先来例子,请看如下代码:

public class HeapOOM {

    static class OOMObj{}

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

        while(true){
            oomObjs.add(new OOMObj());
        }
    }
}

我们通过设置如下JVM参数:

-Xms5m -Xmx5m -XX:+PrintGC

堆的最大值和最小值都设置为5m,避免JVM堆自动扩展,并打印GC信息。

注:Eclipse和Intellij idea都可以设置每次程序运行的JVM的参数,请自行搜索设置。

运行程序结果如下:

[GC (Allocation Failure)  1024K->568K(5632K), 0.0016659 secs]
[GC (Allocation Failure)  1592K->1232K(5632K), 0.0013845 secs]
[GC (Allocation Failure)  2030K->1845K(5632K), 0.0018025 secs]
[GC (Allocation Failure)  3494K->2808K(5632K), 0.0048116 secs]
[GC (Allocation Failure) -- 4770K->5626K(5632K), 0.0063304 secs]
[Full GC (Ergonomics)  5626K->4062K(5632K), 0.0542477 secs]
[Full GC (Ergonomics)  5086K->5064K(5632K), 0.0471644 secs]
[Full GC (Ergonomics)  5084K->5072K(5632K), 0.0313137 secs]
[Full GC (Allocation Failure)  5072K->5060K(5632K), 0.0599175 secs]
Exception in thread "main" 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.example.demo.HeapOOM.main(HeapOOM.java:20)
[Full GC (Ergonomics)  5073K->369K(5632K), 0.0088324 secs]

我们不断的创建对象,并且存储到List中,这样GC Roots到对象之间有可达路径,垃圾回收也无法回收,最后触发了Full GC 也无法释放更多的内存,导致OutOfMemoryError。

Java堆内存溢出是在实际应用中比较常见,要出现此类异常,如果解决呢?

2.内存溢出解决方案

要解决这个问题,我们可以通过如下步骤:

第一步:首先我们首先要dump出JVM堆内存数据,可通过JDK提供的工具jmap,使用如下命令:

jmap -dump:live,format=b,file=heap-dump.bin <pid>

在实际项目中堆内存很大,慎重操作。

第二步:我们要通过工具(如Eclipse Memory Analyzer)对堆内存进行可视化分析。

通过可视化工具我们可以分析出到底是内存泄露 memory leak内存溢出 out of memory

如果是内存泄漏,就是说有很多对象垃圾收集机制无法回收。那么我们需要找到泄漏对象,通过泄漏对象到GC Roots的引用链,就可以找到泄漏对象是什么路径与GC Roots相关联并导致JVM垃圾收集机制无法回收的原因。找到泄漏对象信息,以及GC Roots引用链,就可以定位泄漏代码的位置。

当发现不是内存泄漏,也就是说,所有对象存活都是必要的,那显然,堆内存设置小了,那么我们可以根据实际需要和物理内存大小而调整JVM堆内存大小。或者优化代码。

读后有收获,请打赏。更多精彩内容,请关注微信公众号。有疑问请加QQ交流群:454792501

全部评论

发表评论
更多精彩内容,请关注微信公众号