文章目录
前言
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的配置,敬请期待。