沧海一粟

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

0%

jvm-config

1
2
java -XX:+PrintFlagsFinal -version | grep HeapSize  # 查看堆内存配置的默认值
jmap -heap pid

没有万能的JVM参数配置,如果有的话就可能是内存给大点,代码写好点,其它用默认 (JVM已经优化了很多参数)

常用配置

2C4G容器常见配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-server
-Xcomp
-Xmx2g
-Xms2g
-Xmn800m
-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512M
-Xss256k
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=75
-XX:+HeapDumpOnOutOfMemoryError
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

2C2G激进配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-Dfile.encoding=UTF-8
-Xcomp
-Xmx1400m
-Xms1400m
-Xmn700m
-XX:SurvivorRatio=3
-XX:NewRatio=1
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=128m
-Xss256k
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=75
-XX:+HeapDumpOnOutOfMemoryError
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintTenuringDistribution
-Xloggc:/Users/weikeqin1/jvm_gc.log
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining
-XX:+PrintCompilation

常用的JVM配置

-client The client VM is tuned for reducing start-up time and memory footprint. Invoke it by using the -client JVM command-line option.
-server The server VM is designed for maximum program execution speed. Invoke it by using the -server JVM command-line option.

-Dfile.encoding=UTF-8 文件使用UTF-8编码
-Xms2g = -XX:InitialHeapSize=2g = -XX:MinHeapSize=2g 设置初始堆大小为2G
-Xmx2g = -XX:MaxHeapSize=2g 设置最大堆大小为2G
-Xmn1g 设置新生代大小为1G

-XX:MinHeapFreeRatio=
-XX:MaxHeapFreeRatio=
-XX:NewRatio
-XX:NewSize
-XX:MaxNewSize
-XX:+AggressiveHeap

-Xss256k 设置每个线程的堆栈大小

-XX:LargePageSizeInBytes=128m 内存页的大小不可设置过大, 会影响Perm的大小
-XX:MaxDirectMemorySize=536870912

-XX:+UseFastAccessorMethods 原始类型的快速优化

-XX:PetenureSizeThreshold= 设置直接被分配到老年代的最大阀值

-XX:+TraceClassLoading 打印类加载信息

配置垃圾回收算法

-XX:+UseG1GC 使用G1垃圾收集器

gc配置

-XX:+PrintGC 用于垃圾收集时的信息打印 -verbosegc (which is equivalent to -XX:+PrintGC) sets the detail level of the log to fine.
-XX:+PrintGCDetails 打印GC详细信息 ets the detail level to finer.
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式 245469.1 )
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+HeapDumpBeforeFullGC FullGC前保存堆栈信息 测试环境使用,线上最好别用,小心磁盘被打满
-XX:+HeapDumpAfterFullGC FullGC后保存堆栈信息 测试环境使用,线上最好别用,小心磁盘被打满

-XX:+DisableExplicitGC 禁止代码中显示调用GC
-Xloggc:filename gc日志保存到指定文件 把filename替换成 /home/admin/gc.log

-XX:+HeapDumpOnOutOfMemoryError OOM后保存堆栈信息
-XX:-OmitStackTraceInFastThrow 当大量抛出同样的异常的后,后面的异常输出将不打印堆栈
-XX:ErrorFile=logs/hs_err_pid%p.log

JIT config

JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM会尝试将其编译为机器码,从而提高执行速度。

在 Java8 之前,HotSpot 集成了两个 JIT,用 C1 和 C2 来完成 JVM 中的即时编译。
到了 Java9,AOT 编译器被引入。AOT 是在程序运行前进行的静态编译,这样就可以避免运行时的编译消耗和内存消耗,且 .class 文件通过 AOT 编译器是可以编译成 .so 的二进制文件的。
Java10,一个新的 JIT 编译器 Graal 被引入。Graal 是一个以 Java 为主要编程语言、面向 Java bytecode 的编译器。与用 C++ 实现的 C1 和 C2 相比,它的模块化更加明显,也更容易维护。Graal 既可以作为动态编译器,在运行时编译热点方法;也可以作为静态编译器,实现 AOT 编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-xx:+TieredCompilation  设置分层编译
-cient 使用G1编译器
-server 使用G2编译器
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler 启用Graal即时编译器

–XX:+PrintCompilation 设置JVM打印出刚编译过的相关信息
-XX:CompileCommand='print,*ClassName.methodName'


–XX:ReservedCodeCacheSize= 设置代码缓存大小
-XX:InitialCodeCacheSize= 设置初始代码缓存大小

-XX:CompileThreshold=1024 关闭分层编译的情况下,方法调用计数器的默认阈值,在 C1 模式下是 1500 次,在 C2 模式在是 10000 次

-XX:TieredStopAtLevel=1 在打开分层编译的情况下,Java 虚拟机会在解释执行之后直接由 1 层的 C1 进行编译。
-XX:TieredStopAtLevel=4 在打开分层编译的情况下,Java 虚拟机会在解释执行之后直接由 4 层的 C2 进行编译。


# C2编译器配置
#-XX:Tier4MinInvocationThreshold=200000
#-XX:Tier4InvocationThreshold=300000
#-XX:Tier4CompileThreshold=400000

-XX:MaxInlineLevel=3


-XX:+CICompilerCountPerCPU 设置编译线程的总数目根据处理器数量来动态调整,默认是true,强制设定总编译线程数目时该参数失效
-XX:CICompilerCount=3 设置编译器线程的数量
-Xbatch 设置JIT编译器使用主线程 (日志输出是有序的)

在编译过程中遇到方法调用时,将目标方法的方法体纳入编译范围之中,并取代原方法调用的优化手段,最重要的编译器优化

1
2
#CompileThreshold must be between 0 and 268435455
-XX:CompileThreshold=128

方法调用计数器:用于统计方法被调用的次数
在关闭分层编译的情况下,方法调用计数器的默认阈值在 C1 模式下是 1500 次,在 C2 模式在是 10000 次,可通过 -XX:CompileThreshold 来设定;
而在分层编译的情况下,-XX:CompileThreshold 指定的阈值将失效,此时将会根据当前待编译的方法数以及编译线程数来动态调整。

1
-XX:OnStackReplacePercentage=10000

回边计数器:用于统计一个方法中循环体代码执行的次数,

在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge),该值用于计算是否触发 C1 编译的阈值,
在不开启分层编译的情况下,C1 默认为 13995,C2 默认为 10700,可通过 -XX: OnStackReplacePercentage=N 来设置;
而在分层编译的情况下,-XX: OnStackReplacePercentage 指定的阈值同样会失效,此时将根据当前待编译的方法数以及编译线程数来动态调整。

在一些循环周期比较长的代码段中,当循环达到回边计数器阈值时,JVM 会认为这段是热点代码,JIT 编译器就会将这段代码编译成机器语言并缓存,在该循环时间段内,会直接将执行代码替换,执行缓存的机器语言。

1
-XX:CICompilerCount=2

经常执行的方法,默认情况下,方法体大小小于 325 字节的都会进行内联,我们可以通过 -XX:MaxFreqInlineSize=N 来设置大小值;
不是经常执行的方法,默认情况下,方法大小小于 35 字节才会进行内联,我们也可以通过 -XX:MaxInlineSize=N 来重置大小值

1
2
3
-XX:+PrintCompilation //在控制台打印编译过程信息
-XX:+UnlockDiagnosticVMOptions //解锁对JVM进行诊断的选项参数。默认是关闭的,开启后支持一些特定参数对JVM进行诊断
-XX:+PrintInlining //将内联方法打印出来

CMS垃圾回收器配置

Eden –|
Survior0 Space —- Young Generation
Survior1 Space –|

Old Generation
Permanent Generation

-XX:+UseConcMarkSweepGC 使用CMS垃圾收集器
-XX:+CMSParallelRemarkEnabled 降低标记停顿
-XX:CMSFullGCsBeforeCompaction=*
-XX:+UseCMSCompactAtFullCollection 在FullGC的时候 对年老代的压缩
-XX:CMSInitiatingOccupancyFraction=75 CMS垃圾收集会在老年代被占用75%时被触发
-XX:+UseCMSInitiatingOccupancyOnly 基于运行时收集的数据来启动CMS垃圾收集周期

G1垃圾收集器配置

Ende Space
Survivor Space
Oid Generation

Option and Default Value 描述 Description
-XX:+UseG1GC 使用G1垃圾收集器 Use the Garbage First (G1) Collector
-XX:MaxGCPauseMillis=200 设置G1收集过程目标时间,默认值200ms Sets a target for the maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it.
-XX:InitiatingHeapOccupancyPercent=30 设置触发标记周期的 Java 堆占用率阈值。默认值是45%。这里的java堆占比指的是non_young_capacity_bytes,包括old+humongous Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by GCs that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations (e.g., G1). A value of 0 denotes ‘do constant GC cycles’. The default value is 45.
-XX:NewRatio=2 新生代 老年代 比例 Ratio of new/old generation sizes. The default value is 2.
-XX:SurvivorRatio=8 Eden区 Survivor区 比例 Ratio of eden/survivor space size. The default value is 8.
-XX:MaxTenuringThreshold=15 新生代最大需要经历多少次GC晋升到老年代。 Maximum value for tenuring threshold. The default value is 15.
-XX:ParallelGCThreads=2 设置在垃圾回收器的并行阶段使用的线程数。 Sets the number of threads used during parallel phases of the garbage collectors. The default value varies with the platform on which the JVM is running.
-XX:ConcGCThreads=2 并发垃圾收集器将使用的线程数。 Number of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running.
-XX:G1ReservePercent=10 设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险 Sets the amount of heap that is reserved as a false ceiling to reduce the possibility of promotion failure. The default value is 10.
-XX:G1HeapRegionSize=25 设置Region大小 With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb.

G1 Log

-verbosegc (which is equivalent to -XX:+PrintGC) sets the detail level of the log to fine.
-XX:+PrintGCDetails sets the detail level to finer.
-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest sets the detail level to its finest. Like finer but includes individual worker thread information.
-XX:+PrintGCTimeStamps Shows the elapsed time since the JVM started.
-XX:+PrintGCDateStamps Adds a time of day prefix to each entry.

ZGC 垃圾回收器配置

1
2
3
4
5
6
7
8
9
10
11
-XX:MinHeapSize, -Xms
-XX:InitialHeapSize, -Xms
-XX:MaxHeapSize, -Xmx
-XX:SoftMaxHeapSize
-XX:ConcGCThreads
-XX:ParallelGCThreads
-XX:UseLargePages
-XX:UseTransparentHugePages
-XX:UseNUMA
-XX:SoftRefLRUPolicyMSPerMB
-XX:AllocateHeapAt
1
2
3
4
5
6
7
8
9
10
11
12
13
-XX:ZAllocationSpikeTolerance

-XX:ZCollectionInterval

-XX:ZFragmentationLimit

-XX:ZMarkStackSpaceLimit

-XX:ZProactive

-XX:ZUncommit

-XX:ZUncommitDelay
1
2


Lock config

1
2
3
4
-XX:-UseBiasedLocking //关闭偏向锁
-XX:+UseHeavyMonitors //设置重量级锁
-XX:-UseSpinning //参数关闭自旋锁优化(默认打开)
-XX:PreBlockSpin //参数修改默认的自旋次数。JDK1.7后,去掉此参数,由jvm控制

-XX:-UseBiasedLocking 关闭偏向锁

在高并发场景下,当大量线程同时竞争同一个锁资源时,偏向锁就会被撤销,发生 stop the word 后, 开启偏向锁会带来更大的性能开销

-XX:-RestrictContended

避免伪共享
多线程并发条件下,volite变量导致CPU缓存里的数据和主内存的数据不一致导致CPU缓存失效。

1
-XX:-RestrictContended

避免伪共享是以牺牲内存为代价的

伪共享和 CPU 内部的 Cache 有关,Cache 内部是按照缓存行(Cache Line)管理的,缓存行的大小通常是 64 个字节;CPU 从内存中加载数据 X,会同时加载 X 后面(64-size(X))个字节的数据。
避免伪共享很简单,每个变量x独占一个缓存行、不共享缓存行就可以了,具体技术是缓存行填充。
比如想让 变量x 独占一个缓存行,可以在 变量x 的前后各填充 64-size(x) 个字节,这样就一定能保证 变量x 独占一个缓存行。

由于伪共享问题如此重要,所以 Java 也开始重视它了,比如 Java 8 中,提供了避免伪共享的注解:@sun.misc.Contended,通过这个注解就能轻松避免伪共享(需要设置 JVM 参数 -XX:-RestrictContended)。不过避免伪共享是以牺牲内存为代价的

40 | 案例分析(三):高性能队列Disruptor

逃逸分析

逃逸分析(Escape Analysis)是判断一个对象是否被外部方法引用或外部线程访问的分析技术,编译器会根据逃逸分析的结果对代码进行优化。

1
2
-XX:+DoEscapeAnalysis //开启逃逸分析(jdk1.8默认开启)
-XX:-DoEscapeAnalysis //关闭逃逸分析

标量替换

逃逸分析证明一个对象不会被外部访问,如果这个对象可以被拆分的话,当程序真正执行的时候可能不创建这个对象,而直接创建它的成员变量来代替。将对象拆分后,可以分配对象的成员变量在栈或寄存器上,原本的对象就无需分配内存空间了。这种编译优化就叫做标量替换。

1
2
3
4
5
6
public void foo() {
TestInfo info = new TestInfo();
info.id = 1;
info.count = 99;
...//to do something
}
1
2
3
4
5
public void foo() {
id = 1;
count = 99;
...//to do something
}
1
2
3
4
5
6

-XX:+EliminateLocks //开启锁消除(jdk1.8默认开启)
-XX:-EliminateLocks //关闭锁消除

-XX:+EliminateAllocations //开启标量替换(jdk1.8默认开启)
-XX:-EliminateAllocations //关闭标量替换

References

[1] java官网 java命令用到的JVM参数配置
[2] Chapter 4 Tuning the Java Runtime System
[3] java
[4] 8 Concurrent Mark Sweep (CMS) Collector
[5] JVM实用参数(七)CMS收集器
[6] Java Hotspot G1 GC的一些关键技术
[7] Getting Started with the G1 Garbage Collector
[8] ZGC官网
[9] 关键系统的JVM参数推荐(2018仲夏版)
[10] github.com/alibaba/arthas
[11] github.com/vipshop/vjtools
[12] JVM诊断调优CheatSheet
[13] understanding-jit-compiler-just-in-time-compiler
[14] should-heapdumpbeforefullgc-be-used-in-production-environment
[15]12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
[16] [可能是最全面的G1学习笔记( https://www.jianshu.com/p/a3e6a9de7a5d )
[17] 深入浅出 JIT 编译器
[18] JDK 1.8 Hotspot 虚拟机参数说明