1.堆的结构

2.堆的GC过程

3.堆调优参数简介

之前在 深入理解JVM(一)——JVM简介和运行时数据区结构 一篇文章中,我们简单地介绍过,堆的内存结构:分为新生区,养老区,和永久区(jdk1.8已经将养老区更新为元空间),今天我们来仔细讲解一下堆的内存分布,堆的GC过程以及简单的堆调整参数。
image.png

1.堆的结构

首先,整个堆分为新生代和老年代,新生代占1/3的堆空间(新生代中,伊甸园和辛存者0区和1区又是8:1:1的比例),老年代占2/3的堆空间,元空间(永久代)不在JVM的内存里,在操作系统内存中,如图所示。

image.png

image.png

永久区:它在一个常驻内存区域,用于存放JDK自身所携带的元数据,被装载进此区域的数据是不会被回收掉的, 关闭JVM才会释放该区域。

2.堆的GC过程
GC(Garbage Collection),是在JAVA堆内存区域满了之后,发生的一种垃圾收集行为。当我们不断地new对象的时候,内存的可用区域肯定不断地减少。
每次new的对象,不出意外都会在新生代的伊甸园(Eden)区域分配。

eden区域满之后,会发生什么?

当Eden区域满的时候,我们就会触发一次youngGc,这时,把还活着的对象拷贝到Survivor0区
当Eden区再次触发GC的时候会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到Survivor1区域,同时,把这些对象的年龄+1。

一直循环上述步骤,部分对象会在Survivor0区和1区复制来复制去,如此交换15次,最终如果还是存活,就存入到老年代。

当新生代所有区域内存都不够的时候,就会进行新生代和老年代的两个区域的GC,如果新生代和老年代两个区域的GC发生之后,内存还是不够,就会发生FullGC,就是所有区域的GC,如果内存还是不够,就会发生OOM。

image.png

3.堆调优参数简介

在介绍堆调优参数之前,我们先来介绍两个JAVA方法:

//虚拟机最大使用的内存值,默认为物理内存的1/4     
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"MB");
//虚拟机初始使用物理内存大小,默认物理内存的1/64
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"MB");

用这两个方法,可以打印并且得到当前运行该方法的虚拟机的堆值,我们来打印一下试试:

image.png

我的电脑内存是16GB,两个理论值正是大约在1/4和1/64的区间左右。

接下来我们可以用两个参数来调整java虚拟机堆的大小
-Xms1024m 将堆内存的初始值设置为1024m
-Xmx1024m 将堆内存的最大值设置为1024m

image.png

我们在idea的项目启动处,选择编辑参数
image.png

把-Xms1024m -Xmx1024m 输入到 VM Option里面

image.png

再执行刚刚那两个方法:

image.png

JVM堆大小调整成功!

接下来我们模拟一下OOM,这时要设置一个参数 -XX:+PrintGCDetails

在控制台打印垃圾收集信息

image.png

        String oom = "666";

        while (true) {
            oom += oom + "66666666666666666666666666666666666666666666666666666";
        }

我们只要不断地new对象,内存总有塞不下的一天,同时,我们还可以观察GC的信息并加以分析,我们运行函数。

image.png

出现了我们想要的OOM,我们复制一条垃圾收集信息来分析一下:

[GC (Allocation Failure)
[PSYoungGen: 269254K->776K(304640K)]
这条日志表示GC前young区和GC后young区的内存大小区别 ,GC前:269254 GC后:776
670669K->459534K(1000960K), 0.0303594 secs]
这条日志表示堆的总内存大小,GC前670669,GC后459534K
[Times: user=0.05 sys=0.03, real=0.03 secs]
这条表示执行时间。

再来看一条Full GC
[Full GC (Ergonomics)
[PSYoungGen: 235302K->0K(304640K)]
同上,年轻代收集情况
[ParOldGen: 688134K->344783K(696320K)] 923437K->344783K(1000960K),
老年代收集情况
[Metaspace: 3263K->3263K(1056768K)], 0.0394456 secs]
永久代收集情况
[Times: user=0.13 sys=0.02, real=0.04 secs]
耗时

总结:
今天我们介绍了堆的结构,以及GC过程以及调优参数,我们可以尝试动手地调试一下JVM参数,虽然这只是一些简单的调参。但是通过不断地练习,由浅入深由易到难,我们最后都会掌握JVM调优!


苏凌峰
73 声望38 粉丝

你的迷惑在于想得太多而书读的太少。