前言

我们先来回顾下JVM中堆的相关知识。看下面的图:

从图中可以看出: 堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。

1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Survivor Space(from 和to)。 2)年老代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。 默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。 JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。


下面我们来通过实战代码测试来真实感受下堆的大小区域配置。

环境准备

idea2019.2

jdk1.8

安装插件

我们使用jdk8中自带的jvisualvm.exe工具来查看jvm的一些信息:

双击打开,在左侧可以看到是一些本地运行的Java进程的信息,我们自己写的Java进程也在列表中:

我们需要安装一个可视化的插件,点击工具一栏中的插件选项:

选择其中一个名叫Visual GC的插件,点击安装。安装完成后重启jvisualvm.exe程序,并双击我们的正在运行的YoungOldTest类,在右侧可以看到你Visual GC的界面,如下图所示:

测试

写了一个测试类:

1
2
3
4
5
6
7
public class YoungOldTest {
public static void main(String[] args) throws InterruptedException {

System.out.println("hello");
Thread.sleep(3000000); //为了便于观察,我们让程序保持睡眠一段时间
}
}

运行程序,我们不做任何参数的设置,看下默认情况下的jvm情况:

可以看到 默认情况下 -XX:NewRatio=2 , 标识新生代占1 , 老年代占2 ,新生代占整个堆的1/3 ;

默认情况下Eden空间和另外两个Survivor空间占比分别为8:1:1。实际中也有些许误差。

下面我们来设置jvm的一些参数:

修改**-XX:NewRatio=4**,表示新生代占1,老年代占4,新生代占老年代的1/5。

运行我们的main方法。在工具中查看:

可以看到我们的年轻代和老年代的信息。

前面我们说过默认年轻代中Eden:from:to的大小是8:1:1,但实际中有误差。所以下面我们来主动设置下这个比例:

-Xmx600m -Xms600m -XX:NewRatio=4 -XX:SurvivorRatio=8

Eden : from : to = 8 : 1 : 1 ( 可以通过参数**–XX:SurvivorRatio** 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

设置完之后,启动程序,查看,可以看到我们的配置生效:

总结

我们写了简单的demo来测试jvm中对于堆相关属性的配置,通过jvm自带的可视化插件来查看堆的信息,对于堆有了更加直观的认知。