JVM 堆大小相关参数说明

大家都知道JAVA进程必须运行在Java虚拟机中,当启动一个Java进程的时候,也必须给其给其分配内存方可运行,那么jvm 虚拟机到底给此进程分配了多少内存呢?是否有默认值呢? 我指定的-Xmx、-Xms、-Xmn到底有什么作用呢?本篇文章就是对齐进行讲解。

默认分配多少

根据JVM手册[1],在JDK1.8以上(对于JAVA SE 5.0以及以下,请参考[2]),采用google翻译默认分配如下所示:

  1. 对于client模式:

    1
    2
    3
    4
    5
    6
    7
    默认的最大堆大小是物理内存的一半,最高可达 192 兆字节 (MB),否则为物理内存的四分之一,最高可达 1 吉字节 (GB)。

    例如,如果您的计算机有 128 MB 的物理内存,则最大堆大小为 64 MB,大于或等于 1 GB 的物理内存导致最大堆大小为 256 MB。

    JVM 不会实际使用最大堆大小,除非您的程序创建了足够的对象来需要它。 在 JVM 初始化期间分配的数量要小得多,称为初始堆大小。 此数量至少为 8 MB,否则为物理内存的 1/64,最大为 1 GB 的物理内存大小。

    分配给年轻代的最大空间量是总堆大小的三分之一。
  2. 对于server模式:

    1
    默认初始和最大堆大小在服务器 JVM 上的工作方式与在客户端 JVM 上的工作方式类似,只是默认值可以更高。 在 32 位 JVM 上,如果有 4 GB 或更多的物理内存,默认的最大堆大小可以达到 1 GB。 在 64 位 JVM 上,如果有 128 GB 或更多的物理内存,默认的最大堆大小可以达到 32 GB.

默认堆大小

我们举个例子来验证一下。大家可以看到我们起了一个进程,没有设置任何的参数,那么这个进程的相关堆大小是多少呢?

进程默认堆大小示例

通过jmap -heap ${pid} 可以看到如下所示:

jmap显示堆大小

其各个参数的含义[3]如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Parallel GC with 2 thread(s):采用并行垃圾收集器
MinHeapFreeRatio:JVM最小空闲比率,可由-XX:MinHeapFreeRatio=<n>参数设置,jvm heap 在使用率小于n时,heap进行收缩
MaxHeapFreeRatio:JVM最大空闲比率,可由-XX:MaxHeapFreeRatio=<n>参数设置,jvm heap 在使用率大于n时,heap进行扩张
MaxHeapSize:JVM堆的最大大小,可由-XX:MaxHeapSize=<n>参数设置
NewSize:JVM新生代的默认大小,可由-XX:NewSize=<n>参数设置
MaxNewSize:JVM新生代的最大大小,可由-XX:MaxNewSize=<n>参数设置
OldSize:JVM老生代的默认大小,可由-XX:OldSize=<n>参数设置
NewRatio:新生代:老生代(的大小)= 1:2,可由-XX:NewRatio=<n>参数指定New Generation与Old Generation heap size的比例
SurvivorRatio:survivor:eden = 1:8,即survivor space是新生代大小的1/(8+2),[因为有两个survivor区域],可由-XX:SurvivorRatio=<n>参数设置
PS:survivor和eden是新生代里面的东西
MetaspaceSize:元空间的默认大小,超过此值就会触发Full GC,可由-XX:MetaspaceSize=<n>参数设置
CompressedClassSpaceSize:类指针压缩空间的默认大小,可由-XX:CompressedClassSpaceSize=<n>参数设置
MaxMetaspaceSize:元空间的最大大小,可由-XX:MaxMetaspaceSize=<n>参数设置
G1HeapRegionSize:使用G1垃圾收集器的时候,堆被分割的大小,可由-XX:G1HeapRegionSize=<n>参数设置

PS Young Generation:新生代内存分布,包含eden区和survivor区
Eden Space:eden区内存
From Space:survivor区的from内存
To Space:survivor区的to内存
PS Old Generation:老生代区内存

堆空间分配
可以看到最大堆大小为32210157568。初始分配的堆空间总大小为NewSize + OldSize = 715653120+1431830528=2147483648。

(但是Eden Space+From Space+To Space+PS Old Generation 的总和要小于2147483648,这部分还不是很清楚原因。)

由于我们实验的服务器的总内存是256GB,所以验证了JVM手册里面说的 在 64 位 JVM 上,如果有 128 GB 或更多的物理内存,默认的最大堆大小可以达到 32 GB

同时我们可以通过java -XX:+PrintFlagsFinal -version来看一下本机默认分配的JVM一些参数。与默认相同。

print_flags_final

通过命令 jcmd ${pid} VM.flags 可以再次确认此进程分配内存相关参数。可以看到与预期一致。

jcmd_flags

参数的作用

如果我们启动的时候,指定Xmx,Xms等参数,又如何呢?
设置JVM相关参数的进程

通过java -heap 命令可以看到分配内存如下所示:
java_heap,也是符合上面的规律的。

(但是Eden Space+From Space+To Space+PS Old Generation 的总和要大于NewSize + OldSize,所以通过这两个实验,猜测Eden Space+From Space+To Space+PS Old Generation仅仅是分配的内存,只要小于MaxHeapSize即可。与InitialHeapSize无关)

通过命令 jcmd ${pid} VM.flags 可以再次确认此进程分配内存相关参数。可以看到与预期一致。最大值为10GB,初始堆空间大小为3G。

jcmd_flags

Xmx与Xms如何设置

通过以上分析我们知道,Xmx是最大堆大小限制,Xms是初始堆大小。通过jvm手册[7],可以看到设置Xms与Xmx相等,可以减少GC发生的次数。

java_heap_size_options
所以在以后的实际经验中,可以设置Xms与Xmx一致即可[8]。

参考文献

[1] https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#parallel_collector_excessive_gc
[2] https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gc-ergonomics.html
[3] https://blog.csdn.net/csj50/article/details/122544491
[4] JVM Server与Client运行模式 https://blog.csdn.net/xlxxcc/article/details/52388195
[5] jvm调优 https://blog.csdn.net/weixin_45735355/article/details/121397268
[6] -X Command-line Options https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html
[7] Java HotSpot VM Heap Size Options https://docs.oracle.com/cd/E21764_01/web.1111/e13814/jvm_tuning.htm#PERFM161
[8] JVM的Xms和Xmx参数设置为相同值有什么好处? https://cloud.tencent.com/developer/article/1695047