资讯专栏INFORMATION COLUMN

JVM 堆(heap)溢出案例

leejan97 / 607人阅读

摘要:一说明当虚拟机申请不到内存空间的时候,会报堆内存溢出。记录次数不让进程结束,便于使用分析工具来查看内存情况使用的版本报错信息运行结果表明,运行到次时,出现了堆内存溢出。

一、说明

当虚拟机申请不到内存空间的时候,会报堆内存溢出: OutOfMemoryError:java heap space

常见的原因:http://outofmemory.cn/c/java-...

我测试到时候,运行在 16G 内存的机器上。JVM 堆内存 默认为物理内存的1/4,即 16 * 1/4 = 4G

JDK 8的 JVM 在 JDK 7 的基础上从堆内存中移除了永久代(Perm Generation),替换为了堆内存之外的元空间(Metaspace),元空间是堆外直接内存,不受堆内存的限制,只受物理内存的限制,可以提供更大的空间。
二、原因及解决办法

OutOfMemoryError 异常的常见原因:

加载的数据过大。如:加载的文件或者图片过大、一次从数据库取出过多数据

代码存在死循环或循环产生过多的对象

解决方法

增加jvm的内存大小,使用 -Xmx 和 -Xms 来设置

检查代码中是否有死循环或递归调用。

检查是否有大循环重复产生新对象实体。

检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

检查List、Map等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

三、代码示例
/**java堆溢出实例
 * 原理:java的堆是用来存放对象实例的,所以我们只要做到以下三点就可以使堆溢出:
 * 1、限制堆的大小,不可扩展
 * 2、不断新建对象
 * 3、保持对象存活不被回收
 * 对应的,我们需要:
 * 1、改变JVM的启动参数,将堆的最小值和最大值设成一样,这样就可以避免堆自动扩展(其实不一样也可以)
 * 2、不断产生对象
 * 3、使用一个List来保存对象,保持对象存活
 *
 * JVM配置参数: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 *
 */
public class HeapOom {
    public static void main(String[] args) {
        // 此list实例会存放在堆内存中
        List list = new ArrayList<>();
        int i = 0;
        boolean flag = true;
        while (flag) {
            try {
                i++;
                // 每次增加一个1M大小的数组对象
                list.add(new byte[1024 * 1024]);
            } catch (Throwable e) {  // catch 捕获的是 Throwable,而不是 Exception。因为 OutOfMemoryError 不属于 Exception 的子类。
                e.printStackTrace();
                flag = false;
                // 记录次数
                System.out.println("count=" + i);
            }
        }

        // 不让进程结束,便于使用分析工具来查看内存情况
        try {
            Thread.sleep(24 * 60 * 60 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用的 java 1.8.0_171 版本:

$ java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

报错信息:

count=3316
java.lang.OutOfMemoryError: Java heap space
    at com.song.HeapOom.main(HeapOom.java:21)

运行结果表明,运行到 3316 次时,出现了堆内存溢出。由于每次增加1M内存。粗略估计:程序大约使用3G内存时,出现了内存溢出情况。

通过jmap或VisualVM 者工具可以查看内存情况:最后可以看出,老年代内存使用情况,大约使用了2709MB,使用率近100%。从而导致了 OutOfMemoryError

TIPS:下面查看内存时,由于使用工具查看内存的时间不是同一时间,所以内存使用量有细微差别
1. 使用jmap查看的内存信息
$ jmap -heap 3428
Attaching to process ID 3428, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b18

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4261412864 (4064.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1420296192 (1354.5MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 637009920 (607.5MB)
   used     = 637009920 (607.5MB)
   free     = 0 (0.0MB)
   100.0% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation  // 老年代内存使用情况,大约使用了2709MB,使用率近100%(导致OutOfMemoryError)
   capacity = 2841116672 (2709.5MB)
   used     = 2840991320 (2709.38045501709MB)
   free     = 125352 (0.11954498291015625MB)
   99.99558793198338% used

4955 interned Strings occupying 422328 bytes.

2. 使用 VisualVM 查看的内存信息

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75094.html

相关文章

  • 关于JVM内存溢出的原因分析及解决方案探讨

    摘要:内存溢出分配的内存空间超过系统内存。内存泄漏的原因分析由大块组成堆,栈,本地方法栈,程序计数器,方法区。内存溢出的原因分析内存溢出是由于没被引用的对象垃圾过多造成没有及时回收,造成的内存溢出。小结栈内存溢出程序所要求的栈深度过大导致。 showImg(https://segmentfault.com/img/bVbweuq?w=563&h=300); 前言:JVM中除了程序计数器,其他...

    xuexiangjys 评论0 收藏0
  • 十种JVM内存溢出的情况,你碰到过几种?

    摘要:内存溢出的情况就是从类加载器加载的时候开始出现的,内存溢出分为两大类和。以下举出个内存溢出的情况,并通过实例代码的方式讲解了是如何出现内存溢出的。内存溢出问题描述元空间的溢出,系统会抛出。这样就会造成栈的内存溢出。 导言: 对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不需要自己实现释放内存,不容易出现内存泄漏和内存溢出的问题,由虚拟机管理内存这一切看起来非常美好,但是一旦...

    ShevaKuilin 评论0 收藏0
  • 摘记《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》

    摘要:第章内存区域与内存溢出异常运行时数据区域虚拟机在执行程序的过程中会把它所管理的内存划分为若干个不同的数据区域。即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 第2章 Java内存区域与内存溢出异常 2.2 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。根据《Java虚拟机规范(Java SE 7版)...

    zoomdong 评论0 收藏0
  • 【修炼内功】[JVM] 浅谈虚拟机内存模型

    摘要:也正是因此,一旦出现内存泄漏或溢出问题,如果不了解的内存管理原理,那么将会对问题的排查带来极大的困难。 本文已收录【修炼内功】跃迁之路 showImg(https://segmentfault.com/img/bVbsP9I?w=1024&h=580); 不论做技术还是做业务,对于Java开发人员来讲,理解JVM各种原理的重要性不必再多言 对于C/C++而言,可以轻易地操作任意地址的...

    sanyang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<