我们可以在启动 Java 命令时指定不同的 JVM 参数,让 JVM 调整自己的运行状态和行为,内存管理和垃圾回收的 GC 算法,添加和处理调试和诊断信息等等。
JVM参数选项
类型一:标准参数选项
特点:比较稳定,后续版本基本不会变化,所有的 JVM 都要实现这些参数,并且向后兼容。以-开头。
各种选项:
用法: java [-options] class [args...] (执行类) 或 java [-options] -jar jarfile [args...] (执行 jar 文件) 其中选项包括: -d32 使用 32 位数据模型 (如果可用) -d64 使用 64 位数据模型 (如果可用) -server 选择 "server" VM 默认 VM 是 server, 因为您是在服务器类计算机上运行。 -cp <目录和 zip/jar 文件的类搜索路径> -classpath <目录和 zip/jar 文件的类搜索路径> 用 : 分隔的目录, JAR 档案 和 ZIP 档案列表, 用于搜索类文件。 -D<名称>=<值> 设置系统属性 -verbose:[class|gc|jni] 启用详细输出 -version 输出产品版本并退出 -version:<值> 警告: 此功能已过时, 将在 未来发行版中删除。 需要指定的版本才能运行 -showversion 输出产品版本并继续 -jre-restrict-search | -no-jre-restrict-search 警告: 此功能已过时, 将在 未来发行版中删除。 在版本搜索中包括/排除用户专用 JRE -? -help 输出此帮助消息 -X 输出非标准选项的帮助 -ea[:<packagename>...|:<classname>] -enableassertions[:<packagename>...|:<classname>] 按指定的粒度启用断言 -da[:<packagename>...|:<classname>] -disableassertions[:<packagename>...|:<classname>] 禁用具有指定粒度的断言 -esa | -enablesystemassertions 启用系统断言 -dsa | -disablesystemassertions 禁用系统断言 -agentlib:<libname>[=<选项>] 加载本机代理库 <libname>, 例如 -agentlib:hprof 另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help -agentpath:<pathname>[=<选项>] 按完整路径名加载本机代理库 -javaagent:<jarpath>[=<选项>] 加载 Java 编程语言代理, 请参阅 java.lang.instrument -splash:<imagepath> 使用指定的图像显示启动屏幕 有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation/index.html。
(直接在DOS窗口中运行java或者java -help可以看到所有的标准选项)
JVM 有两种运行模式:
- -server:设置 jvm 使 server 模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有 64 位能力的 jdk 环境下将默认启用该模式,而忽略 -client 参数。
- -client:JDK1.7 之前在 32 位的 x86 机器上的默认值是
-client
选项。设置 jvm 使用 client 模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或者PC应用开发和调试。
示例:
JAVA_OPTS="-server"
类型二:-X参数选项
特点:非标准化参数,功能还是比较稳定的,但官方说后续版本可能会变更。 基本都是传给 JVM 的,默认 JVM 实现这些参数的功能,但是并不保证所有 JVM 实现都满足,且不保证向后兼容。以-X开头。
各种选项:
-Xmixed 混合模式执行 (默认)
-Xint 仅解释模式执行
-Xcomp仅采用即时编译器模式
-Xbootclasspath:
设置搜索路径以引导类和资源
-Xbootclasspath/a:
附加在引导类路径末尾
-Xbootclasspath/p:
置于引导类路径之前
-Xdiag 显示附加诊断消息
-Xnoclassgc 禁用类垃圾收集
-Xincgc 启用增量垃圾收集
-Xloggc: 将 GC 状态记录在文件中 (带时间戳)
-Xbatch 禁用后台编译
-Xms 设置初始 Java 堆大小
-Xmx 设置最大 Java 堆大小
-Xss 设置 Java 线程堆栈大小
-Xprof 输出 cpu 配置文件数据
-Xfuture 启用最严格的检查, 预期将来的默认值
-Xrs 减少 Java/VM 对操作系统信号的使用 (请参阅文档)
-Xcheck:jni 对 JNI 函数执行其他检查
-Xshare:off 不尝试使用共享类数据
-Xshare:auto 在可能的情况下使用共享类数据 (默认)
-Xshare:on 要求使用共享类数据, 否则将失败。
-XshowSettings 显示所有设置并继续
-XshowSettings:all
显示所有设置并继续
-XshowSettings:vm 显示所有与 vm 相关的设置并继续
-XshowSettings:properties
显示所有属性设置并继续
-XshowSettings:locale
显示所有与区域设置相关的设置并继续
-X 选项是非标准选项,如有更改,恕不另行通知
(直接在DOS窗口中运行java -X命令可以看到所有的X选项)
JVM的JIT编译模式相关的选项:
JVM 加载字节码后,可以解释执行,也可以在校验之后通过 JIT 编译器转换为本地代码再执行,所以可以配置 JVM 对字节码的处理模式:
- -Xint:只使用解释器:强制 JVM 解释执行所有的字节码,这个模式的速度是很慢的,通常低 10 倍或更多。
- -Xcomp:只使用编译器:所有字节码第一次使用就被编译成本地代码,然后在执行,从而带来最大程度的优化。
- -Xmixed:混合模式:这是默认模式,也是推荐模式。刚开始的时候使用解释器慢慢解释执行,后来让JIT即时编译器根据程序运行的情况,有选择地将某些热点代码提前编译并缓存在本地,在执行的时候效率就非常高了。 我们使用 java -version 可以看到 mixed mode 等信息。
特别地:
- -Xms<size> 设置初始Java堆大小,等价于-XX:InitialHeapSize
- -Xmx<size> 设置最大Java堆大小,等价于-XX:MaxHeapSize
- -Xss<size> 设置Java线程堆栈大小,等价于-XX:ThreadStackSize
类型三:-XX参数选项
特点:非标准化参数,使用的最多的参数类型,这类选项属于实验性,不稳定,专门用于控制 JVM 的行为,跟具体的 JVM 实现有关,随时可能会在下个版本取消。以-XX开头。
作用:用于开发和调试JVM。
分类:
- Boolean类型格式:-XX:+<option> 表示启用option属性,-XX:-<option>表示禁用option属性。有的指令默认是开启的,所以可以使用-关闭。例如:-XX:UseG1GC表示启用G1收集器。
- 非Boolean类型格式(key-value类型):
- 子类型1:数值型格式-XX:<option>=<number>。number表示数值,可以带上单位,比如:m、M表示兆,k、K表示kb(32k跟32768一样)。-XX:NewSize=1024m,表示设置新生代初始大小为1024兆。
- 子类型2:非数值型格式-XX:<name>=<string>。例如:-XX:HeapDumpPath=/usr/local/heapdump.hprof 用来指定heap转存文件的存储路径。
特别的:
-XX:+PrintFlagsFinal:输出所有参数的名称和默认值,默认不包括Diagnostic和Experimental的参数,可以配合-XX:+UnlockDiagnosticVMOptions和-XX:UnlockExperimentalVMOptions使用
添加JVM参数选项
直接通过命令行启动 Java 程序的格式:
java [options] classname [args]
java [options] -jar filename [args]
其中:
- [options] 部分称为 "JVM 选项",对应 IDE 中的 VM options, 可用jps -v查看。
- [args] 部分是指 "传给main函数的参数", 对应 IDE 中的 Program arguments, 可用jps -m查看。
java -Xms50m -Xmx50m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -jar demo.jar
通过Tomcat运行war包
如果是使用 Tomcat 之类自带 startup.sh 等启动脚本的程序,我们一般把相关参数都放到一个脚本定义的 JAVA_OPTS 环境变量中,最后脚本启动 JVM 时会把 JAVA_OPTS 变量里的所有参数都加到命令的合适位置。
Linux系统下可以在tomcat/bin/catalina.sh中添加类似如下配置:JAVA_OPTS="-Xms512M -Xmx1024M"
Windows系统下载catalina.bat中添加类似如下配置:set "JAVA_OPTS=-Xms512M -Xmx1024M"
IDEA
如果是在 IDEA 之类的 IDE 里运行的话,则可以在“Run/Debug Configurations”里找到 VM 选项和程序参数两处地方,直接输入即可。
上图输入了两个 VM 参数,都是环境变量,一个是指定文件编码使用 UTF-8,一个是设置了环境变量 a 的值为 1。
增加了一个main函数的参数。
程序运行过程中
使用jinfo -flag <name>=<value> <pid>设置非Boolean类型参数
使用jinfo -flag [+|-]<name> <pid>设置Boolean类型参数
常用的JVM参数选项
打印设置的XX选项及值
-XX:+PrintCommandLineFlags:可以让程序运行前打印出用户手动设置或者JVM自动设置的XX选项
-XX:+PrintFlagsInitial:表示打印出所有XX选项的默认值
-XX:+PrintFlagsFinal:表示打印出XX选项在运行程序时生效的值。如果值的前面加上了:=,说明该值不是初始值,该值可能被jvm自动改变了,也可能被我们设置的参数改变了。
-XX:+PrintVMOptions:打印JVM的参数。
堆、栈、方法区等内存大小设置
栈:
Xss128k:等价于-XX:ThreadStackSize,设置每个线程的栈大小为128k
堆内存
JVM 的内存设置是最重要的参数设置,也是 GC 分析和调优的重点。
JVM 总内存=堆+栈+非堆+堆外内存。
-Xms3550m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为3500M。如 -Xms4g。 而且指定的内存大小,并不是操作系统实际分配的初始值,而是 GC 先规划好,用到才分配。 专用服务器上需要保持 -Xms和-Xmx一致,否则应用刚启动可能就有好几个 FullGC。当两者配置不一致时,堆内存扩容可能会导致性能抖动。
-Xmx3550m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为3500M。如 -Xmx4g. 这只是限制了 Heap 部分的最大值为 4g。这个内存不包括栈内存,也不包括堆外使用的内存。
-Xmn2g:设置年轻代大小为2G,即等价于-XX:NewSize=2g -XX:MaxNewSize=2g,也就是设置年轻代初始值和年轻代最大值都是2G。官方推荐配置为整个堆大小的3/8。使用 G1 垃圾收集器 不应该 设置该选项,在其他的某些业务场景下可以设置。官方建议设置为 -Xmx 的 1/2 ~ 1/4。
-XX:NewSize=1024m:设置年轻代初始值为1024M
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M
-XX:SurvivorRatio=8:设置年轻代中Eden区与一个Survivor区的比值,默认为8
-XX:+UseAdaptiveSizePolicy:自动选择各区大小比例,默认开启
-XX:NewRatio=2:设置老年代与年轻代(包括1个Eden区和2个Survivor区)的比值,默认为2
-XX:PretenureSizeThreadshold=1024:设置让大于此阈值的对象直接分配在老年代,单位为字节。只对Serial、ParNew收集器有效
-XX:MaxTenuringThreshold=15:默认值为15。新生代每次MinorGC后,还存活的对象年龄+1,当对象的年龄大于设置的这个值时就进入老年代
-XX:+PrintTenuringDistribution:让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布
-XX:TargetSurvivorRatio:表示MinorGC结束后Survivor区域中占用空间的期望比例
方法区
永久代
-XX:PermSize=256m:设置永久代初始值为256M
-XX:MaxPermSize=256m:设置永久代最大值为256M。这是 JDK1.7 之前使用的。Java8 默认允许的 Meta 空间无限大,此参数无效。
元空间
-XX:MetaspaceSize:初始空间大小
-XX:MaxMetaspaceSize:最大空间,Java8 默认没有限制,一般不允许设置该选项。
-XX:+UseCompressedOops:使用压缩对象指针
-XX:+UseCompressedClassPointers:使用压缩类指针
-XX:CompressedClassSpaceSize:设置Klass Metaspace的大小,默认1G
直接内存
-XX:MaxDirectMemorySize:指定DirectMemory容量,若未指定,则默认与Java堆最大值一样。这个参数跟-Dsun.nio.MaxDirectMemorySize效果相同。
注意
这里要特别说一下堆外内存,也就是说不在堆上的内存,我们可以通过jconsole,jvisualvm 等工具查看。
一个 Java 进程里面,可以分配 native memory 的东西有很多,特别是使用第三方 native 库的程序更是如此。
但在这里面除了
- GC heap = Java heap + Perm Gen(JDK <= 7)
- Java thread stack = Java thread count * Xss
- other thread stack = other thread count * stack size
- CodeCache 等东西之外
还有诸如 HotSpot VM 自己的 StringTable、SymbolTable、SystemDictionary、CardTable、HandleArea、JNIHandleBlock 等许多数据结构是常驻内存的,外加诸如 JIT 编译器、GC 等在工作的时候都会额外临时分配一些 native memory,这些都是 HotSpot VM自己所分配的 native memory;在 JDK 类库实现中也有可能有些功能分配长期存活或者临时的 native memory。
然后就是各种第三方库的 native 部分分配的 native memory。
“Direct Memory”,一般来说是 Java NIO 使用的 Direct-X-Buffer(例如 DirectByteBuffer)所分配的 native memory,这个地方如果我们使用 netty 之类的框架,会产生大量的堆外内存。
示例:
JAVA_OPTS="-Xms6g -Xmx6g"
最佳实践
配置多少 xmx 合适
从上面的分析可知,系统有大量的地方使用堆外内存,远比我们常说的 xmx 和 xms 包括的范围要广。所以我们需要在设置内存的时候留有余地。
实际上,推荐配置系统或容器里可用内存的 70-80% 最好。比如说系统有 8G 物理内存,系统自己可能会用掉一点,大概还有 7.5G 可以用,那么建议配置
-Xmx6g 说明:xmx : 7.5G*0.8 = 6G,如果知道系统里有明确使用堆外内存的地方,还需要进一步降低这个值。
xmx 和 xms 是不是要配置成一致的
一般情况下,我们的服务器是专用的,就是一个机器(也可能是云主机或 docker 容器)只部署一个 Java 应用,这样的时候建议配置成一样的,好处是不会再动态去分配,如果内存不足上来就知道。
OutOfMemory相关的选项
-XX:+HeapDumpOnOutOfMemoryError:表示在内存出现OOM的时候,生成Heap转储文件,以便后续分析,-XX:+HeapDumpBeforeFullGC和-XX:+HeapDumpOnOutOfMemoryError只能设置1个。因为在运行时并没有什么开销, 所以在生产机器上是可以使用的。 示例用法: java -XX:+HeapDumpOnOutOfMemoryError -Xmx256m ConsumeHeap
-XX:+HeapDumpBeforeFullGC:表示在出现FullGC之前,生成Heap转储文件,以便后续分析,-XX:+HeapDumpBeforeFullGC和-XX:+HeapDumpOnOutOfMemoryError只能设置1个,请注意FullGC可能出现多次,那么dump文件也会生成多个
-XX:HeapDumpPath=<path>:指定heap转存文件的存储路径,如果没有指定则默认为启动 Java 程序的工作目录。 示例用法: java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D: ConsumeHeap 自动 Dump 的 hprof 文件会存储到D: 目录下。
-XX:OnOutOfMemoryError:指定一个可行性程序或者脚本的路径,当发生OOM的时候,去执行这个脚本
-XX:ErrorFile=filename 选项, 致命错误的日志文件名,绝对路径或者相对路径。
-XX:OnError 选项, 发生致命错误时(fatal error)执行的脚本。 例如, 写一个脚本来记录出错时间, 执行一些命令, 或者 curl 一下某个在线报警的url. 示例用法: java -XX:OnError="gdb - %p" MyApp可以发现有一个 %p 的格式化字符串,表示进程 PID。
垃圾收集器相关选项
- -XX:+PrintCommandLineFlags:查看命令行相关参数(包含使用的垃圾收集器)
- 使用命令行指令:jinfo -flag 相关垃圾回收器参数 进程ID
以上两种方式都可以查看默认使用的垃圾回收器,第一种方式更加准备,但是需要程序的支持;第二种方式需要去尝试,如果使用了,返回的值中有+号,否则就是-号
Serial回收器
Serial收集器作为HotSpot中Client模式下的默认新生代垃圾收集器。Serial Old是运行在Client模式下的默认的老年代的垃圾回收器。
-XX:+UseSerialGC
指定老年代和年轻代都是要串行收集器。等价于新生代使用SerialGC,老年代使用Serial Old GC。获得最高的单线程收集效率。
Parnew回收器
-XX:+UseParNewGC
手动指定使用ParNew收集器执行内存回收任务。它表示年轻代使用并行收集器,不影响老年代。
Parallel回收器
-XX:+UseParallelGC 手动指定年轻代使用Parallel并行收集器执行内存回收任务。
-XX:+UseParallelOldGC 手动指定老年代使用并行回收收集器。分别适用于年轻代和老年代。默认JDK8是开启的。它和-XX:+UseParallelGC 互相激活,开启一个,另外一个也会被开启。
-XX:ParallelGCThreads 设置年轻代并行收集器的线程数。一般地,最好与CPU核心数量相等,以避免过多的线程数影响垃圾收集性能。在默认情况下,当CPU核心的数量小于8个,ParallelGCThreads的值等于CPU核心数量。当CPU核心数量大于8个,ParallelGCThreads的值等于3+[5*core_count]/8
-XX:MaxGCPauseMillis 设置垃圾收集器最大停顿时间(即STW的时间)。单位为毫秒。为了尽可能地把停顿时间控制在MaxGCpauseMillis以内,收集器在工作时会调整Java堆大小或者其他一些参数。对于用户来讲,停顿时间越短体验越好。但是在服务器端,我们注重高并发,整体的吞吐量。所以服务器端适合Parallel,进行控制。
-XX:GCTimeRatio 垃圾收集时间占总时间的比例(= 1/(N+1))。用于衡量吞吐量的大小。取值范围(0,100)。默认99,也就是垃圾回收时间不超过1%。与前一个 -XX:MaxGCPauseMillis 参数有一定矛盾性。暂停时间越长,Ratio参数就容易超过设定的比例。
-XX:UseAdaptiveSizePolicy 设置Parallel Scavenge 收集器具有自适应调节策略。在这种模式下,年轻代的大小、Eden和Survivor的比例、晋升老年代的对象年龄等参数会被自动调整,已达到在堆大小、吞吐量和停顿时间之间的平衡点。在手动调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆、目标的吞吐量(GCTimeRatio )和停顿时间(MaxGCPauseMillis),让虚拟机自己完成调优工作。
注意:
- Parallel回收器主打吞吐量,而CMS和G1主打低延迟,如果主打吞吐量,那么就不应该限制最大停顿时间,所以-XX:MaxGCPauseMills不应该设置
- -XX:MaxGCPauseMills中的调整堆大小通过默认开启的-XX:+UseAdaptiveSizePolicy来实现
- -XX:GCTimeRatio用来衡量吞吐量,并且和-XX:MaxGCPauseMills矛盾,因此不会同时使用
CMS回收器
-XX:+UseConcMarkSweepGC 手动指定使用CMS收集器执行内存回收任务。开启该参数后悔自动将 -XX:+UseParNewGC打开。即:ParNew(年轻代)+ CMS(老年代)+ Serial Old的组合。
-XX:CMSInitiatingOccupancyFraction 设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收。JDK5及以前版本的默认值为68,即当老年代的空间使用率达到68%时,会执行一次CMS回收。JDK6及以后的版本默认值为92。查看CMSInitiatingOccupancyFraction的初始值为-1,查看jvm源码可知,如果CMSInitiatingOccupancyFraction在0~100之间,那么由CMSInitiatingOccupancyFraction决定。否则由按 ((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0 决定。MinHeapFreeRatio,CMSTriggerRatio的初始值分别为40和80,即当老年代达到 ((100 - 40) + (double) 80 * 40 / 100 ) / 100 = 92 %时,会触发CMS回收。如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少老年代回收的次数,可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁地触发老年代串行收集器。因此通过该选项可以有效降低Full GC的执行次数。
-XX:+UseCMSCompactAtFullCollection 开启CMS的压缩,用于指定在执行完Full GC后对内存空间进行压缩整理,以此避免内存碎片的产生。不过由于内存压缩整理过程无法并发执行,所带来的问题就是停顿时间变得更长了。配合 CMSFullGCsBeforeCompaction参数使用。
-XX:CMSFullGCsBeforeCompaction 设置在执行多少次Full GC后对内存空间进行压缩整理,默认为0。配合 UseCMSCompactAtFullCollection 参数一起使用
-XX:ConcGCThreads=n (早期JVM版本也叫-XX:ParallelCMSThreads)定义CMS运行时的线程数。比如value=4意味着CMS周期的所有阶段都以4个线程来执行。尽管更多的线程会加快并发CMS过程,但其也会带来额外的同步开销。因此,对于特定的应用程序,应该通过测试来判断增加CMS线程数是否真的能够带来性能的提升。如果该标志未设置,JVM会根据并行收集器中的-XX:ParallelGCThreads参数的值来计算出默认的并行CMS线程数。该公式是ConcGCThreads = (ParallelGCThreads + 3)/4,向下取整。因此,对于CMS收集器,-XX:ParallelGCThreads 标志不仅影响“stop-the-world”垃圾收集阶段,还影响并发阶段。
ParallelCMSThreads 是年轻代并行收集器的线程数。默认情况下,ParallelCMSThreads等于CPU核心数。当CPU资源比较紧张时,受到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。当CPU核心数超过4个时,GC线程会占用不到25%的CPU资源,如果CPU数不足4个,GC线程对程序的影响就会非常大。
-XX:+UseCMSInitiatingOccupancyOnly:是否动态可调,可用这个参数使CMS一直按CMSInitiatingOccupancyFraction设定的值启动。
-XX:+CMSScavengeBeforeRemark:强制HotSpot在cms remark阶段之前做一次minor gc,用于提高remark阶段的速度。
-XX:+CMSClassUnloadingEnabled:CMS 默认不会对 MetaSpace 或 Perm 进行垃圾收集,设置此参数会对这些区域进行垃圾收集
-XX:+ParallelRefProcEnabled:可以用来并行处理 Reference,以加快处理速度,缩短耗时
-XX:CMSScavengeBeforeRemark:表示开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试,它可以降低 remark 时间,建议加上。
G1回收器
-XX:+UseG1GC 手动指定使用G1收集器执行内存回收任务
-XX:G1HeapRegionSize 设置每个Region的大小。值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。
-XX:MaxGCPauseMillis 设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到)。默认值是200ms。一般来说,回收阶段占到几十到一百甚至接近两百毫秒都很正常,但如果我们把停顿时间调得非常低,譬如设置为二十毫秒,很可能出现的结果就是由于停顿目标时间太短,导致每次选出来的回收集只占堆内存很小的一部分,收集器收集的速度逐渐跟不上分配器分配的速度,导致垃圾慢慢堆积。很可能一开始收集器还能从空闲的堆内存中获得一些喘息的时间,但应用运行时间一长就不行了,最终占满堆引发Full GC反而降低性能,所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的。
-XX:ParallelGCThreads 设置STW期间,并行GC工作线程数。
-XX:ConcGCThreads 并发标记阶段,设置并发执行的线程数。为并行垃圾回收线程数(ParallelGCThreads)的1/4左右,四舍五入。
-XX:InitiatingHeapOccupancyPercent 设置触发标记周期的Java堆占用率阈值。超过此值,就触发GC。默认是45。这里的java堆占比指的是non_young_capacity_bytes,包括old+humongous。值 0 表示不间断的 GC 循环。
-XX:G1NewSizePercent年轻代最小值,默认值5%
-XX:G1MaxNewSizePercent年轻代最大值,默认值60%
-XX:G1MixedGCLiveThresholdPercent=n:一个分区是否会被放入mix GC的CSet的阈值。对于一个分区来说,它的存活对象率如果超过这个比例,则改分区不会被列入mixed gc的CSet中JDK1.6和1.7是65,JDK1.8是85
-XX:G1HeapWastePercent=n:设置您愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比,JavaHotSpotVM不会启动混合垃圾回收周期(注意,这个参数可以用于调整混合收集的频率)。JDK1.8是5
-XX:G1MixedGCCountTarget=8设置并发周期后需要执行多少次混合收集,如果混合收集中STW的时间过长,可以考虑增大这个参数。(注意:这个可以用来调整每次混合收集中回收掉老年代分区的多少,即调节混合收集的停顿时间)8
-XX:G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old generation region数量。
GC日志相关选项
在生产环境或性能压测环境里,我们用来分析和判断问题的重要数据来源之一就是 GC 日志,JVM 启动参数为我们提供了一些用于控制 GC 日志输出的选项。
-verbose:gc:输出日志信息,默认输出的标准输出,可以独立使用。也可以和其他 GC 参数组合使用, 在 GC 日志中输出详细的GC信息。 包括每次 GC 前后各个内存池的大小,堆内存的大小,提升到老年代的大小,以及消耗的时间。此参数支持在运行过程中动态开关。比如使用 jcmd, jinfo, 以及使用 JMX 技术的其他客户端。
-XX:+PrintGC:等同于-verbose:gc,表示打开简化的日志,可以独立使用
-XX:+PrintGCDetails:在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域的分配情况,可以独立使用
-XX:+PrintGCTimeStamps:程序启动到GC发生的时间秒数,不可以独立使用,需要配合-XX:+PrintGCDetails使用
-XX:+PrintGCDateStamps:输出GC发生时的时间戳(以日期的形式,例如:2013-05-04T21:53:59.234+0800),不可以独立使用,可以配合-XX:+PrintGCDetails使用
-XX:+PrintHeapAtGC:每一次GC前和GC后,都打印堆信息,可以独立使用
-XIoggc:<file>:把GC日志写入到一个文件中去,而不是打印到标准输出中
-XX:TraceClassLoading:监控类的加载
-XX:PrintGCApplicationStoppedTime:打印GC时线程的停顿时间
-XX:+PrintGCApplicationConcurrentTime:垃圾收集之前打印出应用未中断的执行时间
-XX:+PrintReferenceGC:记录回收了多少种不同引用类型的引用
-XX:+PrintTenuringDistribution:让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布
-XX:+UseGCLogFileRotation:启用GC日志文件的自动转储
-XX:NumberOfGCLogFiles=1:GC日志文件的循环数目
-XX:GCLogFileSize=1M:控制GC日志文件的大小
-Xloggc:filePath:与 -verbose:gc 功能类似,只是将每次 GC 事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。若与 verbose:gc 命令同时出现在命令行中,则以 -Xloggc 为准。
-XX:-OmitStackTraceInFastThrow:JVM 缩简日志输出。开启这个参数之后,如果多次发生空指针异常,将会打印多条 java.lang.NullPointerException。在实际生产中,这个参数是默认开启的,这样就导致有时候排查问题非常不方便,如果把它关闭,会输出所有的异常堆栈,日志会多很多。
示例:
export JAVA_OPTS="-Xms6g -Xmx6g -Xss1m \
-verbose:gc -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintTenuringDistribution -Xloggc:/tmp/logs/gc_%p.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/logs -XX:ErrorFile=/tmp/logs/hs_error_pid%p.log -XX:-OmitStackTraceInFastThrow"
设置系统属性
当我们给一个 Java 程序传递参数,最常用的方法有两种:
- 系统属性,有时候也叫环境变量,例如直接给 JVM 传递指定的系统属性参数,需要使用 -Dkey=value 这种形式,此时如果系统的环境变量里不管有没有指定这个参数,都会以这里的为准。
- 命令行参数,直接通过命令后面添加的参数,比如运行 Hello 类,同时传递 2 个参数 kimm、king:java Hello kimm king,然后在Hello类的 main 方法的参数里可以拿到一个字符串的参数数组,有两个字符串,kimm 和 king。
比如我们常见的设置 $JAVA_HOME 就是一个环境变量,只要在当前命令执行的上下文里有这个环境变量,就可以在启动的任意程序里,通过相关 API 拿到这个参数,比如 Java 里:
System.getProperty("key") 来获取这个变量的值,这样就可以做到多个不同的应用进程可以共享这些变量,不用每个都重复设置,也可以实现简化 Java 命令行的长度(想想要是配置了 50 个参数多恐怖,放到环境变量里,可以简化启动输入的字符)。此外,由于环境变量的 key-value 的形式,所以不管是环境上下文里配置的,还是通过运行时-D来指定,都可以不在意参数的顺序,而命令行参数就必须要注意顺序,顺序错误就会导致程序错误。
例如:
-Duser.timezone=GMT+08 // 设置用户的时区为东八区 -Dfile.encoding=UTF-8 // 设置默认的文件编码为UTF-8
查看默认的所有系统属性,可以使用命令:
java -XshowSettings:properties -version
cmd显示:
java -XshowSettings:properties -version Property settings: awt.toolkit = sun.awt.windows.WToolkit file.encoding = GBK file.encoding.pkg = sun.io file.separator = \ java.awt.graphicsenv = sun.awt.Win32GraphicsEnvironment java.awt.printerjob = sun.awt.windows.WPrinterJob java.class.path = . java.class.version = 52.0 。。。。。。省略
同样可以查看 VM 设置:
java -XshowSettings:vm -version
VM settings: Max. Heap Size (Estimated): 3.51G Ergonomics Machine Class: client Using VM: Java HotSpot(TM) 64-Bit Server VM 。。。。。。
查看当前 JDK/JRE 的默认显示语言设置:
java -XshowSettings:locale -version
Locale settings: default locale = 中文 default display locale = 中文 (中国) default format locale = 中文 (中国) 。。。。。。
等等,很多地方会用设置系统属性的方式去传递数据给Java程序,而不是直接用程序参数的方式。
Agent 相关的选项
Agent 是 JVM 中的一项黑科技, 可以通过无侵入方式来做很多事情,比如注入 AOP 代码,执行统计等等,权限非常大。这里简单介绍一下配置选项,详细功能在后续章节会详细讲。
设置 agent 的语法如下:
- -agentlib:libname[=options] 启用native方式的agent, 参考LD_LIBRARY_PATH路径。
- -agentpath:pathname[=options] 启用native方式的agent。
- -javaagent:jarpath[=options]启用外部的agent库, 比如 pinpoint.jar 等等。
- -Xnoagent 则是禁用所有 agent。
以下示例开启 CPU 使用时间抽样分析:
JAVA_OPTS="-agentlib:hprof=cpu=samples,file=cpu.samples.log"
其中 hprof 是 JDK 内置的一个性能分析器。cpu=samples
会抽样在各个方法消耗的时间占比, Java 进程退出后会将分析结果输出到文件。
其他参数
-XX:+DisableExplicitGC:禁用hotspot执行System.gc(),默认禁用
-XX:ReservedCodeCacheSize=<n>[g|m|k]、-XX:InitialCodeCacheSize=<n>[g|m|k]:指定代码缓存的大小
-XX:+UseCodeCacheFlushing:使用该参数让jvm放弃一些被编译的代码,避免代码缓存被占满时JVM切换到interpreted-only的情况
-XX:+DoEscapeAnalysis:开启逃逸分析
-XX:+UseBiasedLocking:开启偏向锁
-XX:+UseLargePages:开启使用大页面
-XX:+PrintTLAB:打印TLAB的使用情况
-XX:TLABSize:设置TLAB大小
-XX:+AlwaysPreTouch:表示在启动时就把参数里指定的内存全部初始化,启动时间会慢一些,但运行速度会增加。
-XX:AutoBoxCacheMax:用于加大 IntegerCache。
-Djava.security.egd=file:/dev/./urandom:这个参数使用 urandom 随机生成器,在进行随机数获取时,速度会更快。
-XX:-OmitStackTraceInFastThrow:用于减少异常栈的输出,并进行合并。虽然会对调试有一定的困扰,但能在发生异常时显著增加性能。
通过代码获取JVM参数
Java提供了java.lang.management包用于监视和管理JVM和java运行时中的其他组件,它允许本地和远程监控和管理运行的Java虚拟机。另外Runtime类也可以获取一些内存,CPU核数等相关数据。
通过这些api可以监控我们的应用服务器的堆内存使用情况,设置一些阈值进行报警等处理