闲谈 JVM(一):浅析 JVM Heap 参数配置_wtopps 的博客 - CSDN 博客_jvm maxheapsize

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

文章目录

*  

  * [Default Heap Size](#Default_Heap_Size_110)
  * [Client JVM Default Initial and Maximum Heap Sizes](#Client_JVM_Default_Initial_and_Maximum_Heap_Sizes_116)
  * [Server JVM Default Initial and Maximum Heap Sizes](#Server_JVM_Default_Initial_and_Maximum_Heap_Sizes_128)

前言

JVM 是 Java 语言的核心基石所在,它为 Java 提供了强大的跨平台能力,关于 JVM 的内部结构,想必您并不陌生,有大量的文章来介绍 JVM 的内部组成结构,本篇的重点不在于此,这里假定您对 JVM 的内部组成结构已经比较了解。

JVM 中提供了大量的配置参数,通过 JVM 的参数配置,可以让 JVM 的性能更加适配于应用服务,发挥出更加强大的性能,那么本篇,就来简单聊一下 JVM 的参数配置,首先,来看一下 JVM 堆区的配置。

JVM 内存模型

https://img-blog.csdnimg.cn/20200522150731202.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d0b3Bwcw==,size_16,color_FFFFFF,t_70

上图就是 JVM 的内存模型,JVM 内存结构主要有三大块:堆内存方法区

对于大多数应用来说,Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存

堆内存是 JVM 中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden 空间From Survivor 空间To Survivor 空间

我们首先来看其中最为重要的一个部分,堆(Heap)区。

堆(Heap)配置

按照官方的说法:“Java 虚拟机具有一个堆 (Heap),堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”

堆区的大小,主要由以下几个参数进行控制:

1、Xms

2、Xmx

3、MaxHeapSize

4、InitalHeapSize

我们来分别看一下这几个参数各自的用途。

Xms 与 InitialHeapSize

Xms 等价于 InitialHeapSize,表示 Heap 的初始化大小,即 JVM 启动时,堆区的最小值,使用该参数的正确姿势是:

1
2
3
-Xms10m

-XX:InitialHeapSize=10m

那么这个 Heap 的最小值的设定的意义,即 JVM 进行 GC 垃圾回收时,会对 Heap 进行清理,会对 Heap 的内存进行缩容的操作,那么缩容最小是缩到多小,这个值就是缩容可以缩到的最小内存值。

一般在实际的生产应用中,Xms 的大小配置,一般与 Xmx 设置为同一个值,避免 JVM GC 执行时频繁进行扩容缩容操作。

需要注意的是,InitialHeapSize 的最小值是 1M,如果小于这个值,JVM 启动会报错。

虽然 Xms 与 InitialHeapSize 表示的含义是相同的,但是这两个参数如果同时设置,那么生效的只有最后设置的参数。

Xmx 与 MaxHeapSize

Xmx 等价于 MaxHeapSize,表示 Heap 的最大值大小,即 Heap 区可以分配使用最大内存值,使用该参数的正确姿势是:

1
2
3
-Xmx100m

-XX:MaxHeapSize=100m

这里需要注意的是,MaxHeapSize 与 InitialHeapSize 的关系,MaxHeapSize 是必须要大于等于 InitialHeapSize 的,否则 JVM 无法启动,我们来验证一下这个说法是否正确。

实验,我们来写一个简单的 Demo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class HelloWorld {
	public static void main(String[] args) {
		try {
			Thread.sleep(1000 * 60);
		} catch(Exception e) {
			System.out.println("Error");
		}
		System.out.println("hello world");
	}
}

设定 JVM 参数:

1
-Xms100m -Xmx10m

运行:

1
java -Xms100m -Xmx10m  HelloWorld

执行结果:

1
2
Error occurred during initialization of VM
Initial heap size set to a larger value than the maximum heap size

Heap 的缺省配置

上面我们介绍了 Heap 的大小设置的参数的使用规则,那么如果我们没有设置 Heap 的大小,JVM 会如何设定呢?

我们来看一下 Java8 中 Oracle 的官方说法:

Default Heap Size

Unless the initial and maximum heap sizes are specified on the command line, they are calculated based on the amount of memory on the machine.

Client JVM Default Initial and Maximum Heap Sizes

The default maximum heap size is half of the physical memory up to a physical memory size of 192 megabytes (MB) and otherwise one fourth of the physical memory up to a physical memory size of 1 gigabyte (GB).

For example, if your computer has 128 MB of physical memory, then the maximum heap size is 64 MB, and greater than or equal to 1 GB of physical memory results in a maximum heap size of 256 MB.

The maximum heap size is not actually used by the JVM unless your program creates enough objects to require it. A much smaller amount, called the initial heap size, is allocated during JVM initialization. This amount is at least 8 MB and otherwise 1/64th of physical memory up to a physical memory size of 1 GB.

The maximum amount of space allocated to the young generation is one third of the total heap size.

Server JVM Default Initial and Maximum Heap Sizes

The default initial and maximum heap sizes work similarly on the server JVM as it does on the client JVM, except that the default values can go higher. On 32-bit JVMs, the default maximum heap size can be up to 1 GB if there is 4 GB or more of physical memory. On 64-bit JVMs, the default maximum heap size can be up to 32 GB if there is 128 GB or more of physical memory. You can always set a higher or lower initial and maximum heap by specifying those values directly; see the next section.

根据 Oracle 官方文档的说法,JVM 的默认堆大小如果未指定,它将会根据服务器物理内存计算而来的。

client 模式下,JVM 初始和最大堆大小为: 在物理内存达到 192MB 之前,JVM 最大堆大小为物理内存的一半,否则,在物理内存大于 192MB,在到达 1GB 之前,JVM 最大堆大小为物理内存的 1/4,大于 1GB 的物理内存也按 1GB 计算,举个例子,如果你的电脑内存是 128MB,那么最大堆大小就是 64MB,如果你的物理内存大于或等于 1GB,那么最大堆大小为 256MB。 Java 初始堆大小是物理内存的 1/64,但最小是 8MB。

server 模式下: 与 client 模式类似,区别就是默认值可以更大,比如在 32 位 JVM 下,如果物理内存在 4G 或更高,最大堆大小可以提升至 1GB,,如果是在 64 位 JVM 下,如果物理内存在 128GB 或更高,最大堆大小可以提升至 32GB。

堆(Heap)的动态调整

JVM 在启动之后,整个 Heap 的大小虽然是固定的,但是并不代表整个堆里的内存都可用,在 GC 之后会根据一些参数进行动态的调整,比如我们设置 Xmx 和 Xms 不一样的时候,就表示堆里的新生代和老生代的可用内存都是存在不断变化的。

所以需要提一个概念,叫做相关堆的有效内存,这里的相关堆可以是指新生代,也可以是老生代,甚至整个堆,有效内存表示真正可用的内存。

一般来说,稳定的堆大小对垃圾回收是有利的。获得一个稳定的堆大小的方法是使 - Xms 和 - Xmx 的大小一致,即最大堆和最小堆 (初始堆) 一样。

如果这样设置,系统在运行时堆大小理论上是恒定的,稳定的堆空间可以减少 GC 的次数。因此,很多服务端应用都会将最大堆和最小堆设置为相同的数值。

但是,一个不稳定的堆并非毫无用处。稳定的堆大小虽然可以减少 GC 次数,但同时也增加了每次 GC 的时间。让堆大小在一个区间中震荡,在系统不需要使用大内存时,压缩堆空间,使 GC 应对一个较小的堆,可以加快单次 GC 的速度。基于这样的考虑,JVM 还提供了两个参数用于压缩和扩展堆空间。

-XX:MinHeapFreeRatio 参数用来设置堆空间最小空闲比例,默认值是 40。当堆空间的空闲内存小于这个数值时,JVM 便会扩展堆空间。

-XX:MaxHeapFreeRatio 参数用来设置堆空间最大空闲比例,默认值是 70。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆。

不过需要注意的是,当 - Xmx 和 - Xms 相等时,-XX:MinHeapFreeRatio 和 - XX:MaxHeapFreeRatio 两个参数无效。

在实际生产环境中,如果不是对 JVM Heap 的情况极为了解,不建议设置该参数。

仍是建议将 - Xms 与 - Xmx 的大小设置为一样值,避免 Heap 的大小扩张缩小。

Heap 大小配置建议

在生产环境中,我们的应用上线之前,一定是需要 JVM 参数的配置,那么,JVM 的 Heap 区大小的设置,多大是合理的值呢?

这里我们给出三种常见的服务器配置,对应的 Heap 的配置参数,仅供参考。

1、4 核 8G Linux64 位 JDK8

1
2
-Xmx5440M -Xms5440M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M 
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled

2、2 核 4G Linux64 位 JDK8

1
2
3
4
-Xmx2688M -Xms2688M -Xmn960M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M 
-XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSClassUnloadingEnabled 
-XX:+ParallelRefProcEnabled -XX:+CMSScavengeBeforeRemark

3、4 核 16G Linux64 位 JDK8

1
2
-Xmx10880M -Xms10880M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M 
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled

结语

本篇,我们介绍了 JVM Heap 的相关配置参数,Heap 的配置可以说是 JVM 参数配置中最重要的部分之一,Heap 大小需要根据实际服务器与应用的情况来综合考量,过大的堆区会对 GC 带来较大的压力,从而导致 STW 时间加长,过小的堆区会导致内存空间不足,JVM 需要频繁的 GC 来清理空间,也可能导致 OOM,因此合理的 Heap 大小是非常重要的。

下一篇中,我们会对 Heap 进一步深入,看一下 Heap 的重要组成部分,新生代 Heap 的配置,敬请期待。