0
点赞
收藏
分享

微信扫一扫

闲谈JVM(一):浅析JVM Heap参数配置

吃面多放酱 2022-04-14 阅读 71
java后端

文章目录

前言

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

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

JVM内存模型

堆区示意图

上图就是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启动时,堆区的最小值,使用该参数的正确姿势是:

-Xms10m

-XX:InitialHeapSize=10m

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

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

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

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

Xmx与MaxHeapSize

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

-Xmx100m

-XX:MaxHeapSize=100m

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

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

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参数:

-Xms100m -Xmx10m

运行:

java -Xms100m -Xmx10m  HelloWorld

执行结果:

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的官方说法:

根据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 还提供了两个参数用于压缩和扩展堆空间。

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

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

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

Heap大小配置建议

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

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

1、4核8G Linux64位 JDK8

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

2、2核4G Linux64位 JDK8

-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

-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的配置,敬请期待。

举报

相关推荐

0 条评论