目录
JVM参数选项类型介绍
关于JDK8的选项参数可以参考官网:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html,另外关于-XX的选项可以看下https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html,官网介绍的很详细,本节不可能把每个参数都做下介绍,只介绍一些常用的,没用过的参数在前面的官方网页上查一下就行,里面注释很详细。
JVM选项分为三种:
- 标准选项:比较稳定,后续版本基本不会变化,以-开头
- -X选项(非标准选项):以-X开头,一般还是很稳定的,官方说法后续版本可能会出现变更
- -XX选项(非标准选项):以-X开头,属于实验性,主要是给JVM开发者用于开发和调试JVM的,在后续的版本中行为有可能会变化
标准参数选项
这类选项的功能是很稳定的,在后续版本中也不太会发生变化。运行java或者java -help可以看到所有的标准选项。所有的标准选项都是以-开头,比如-version,-server,-client等。
java -version可以查看JDK版本,运行模式(server/client),JIT编译模式等信息。
C:\Users\Administrator>java -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
Hotspot JVM有两种模式,分别是server和client,分别通过-server和-client模式设置
- 在32位Windows系统上,不论硬件配置如何,都默认使用Client类型的JVM。其他32位操作系统上,要求机器配置至少有2个以上的CPU和2G以上的内存,则默认会使用Server类型的JVM。 client模式适用于对内存要求较小的桌面应用程序,默认使用Serial串行垃圾收集器
- 64位机器上只支持server模式的JVM,适用于需要大内存的应用程序,默认使用并行垃圾收集器
关于server和client的官网介绍为:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/server-class.html
-X参数选项
这类选项都是以-X开头,可能由于这个原因它们被称为X选项。运行java -X命令可以看到所有的X选项。这类参数的功能还是很稳定,但官方的说法是它们的行为可能会在后续版本中改变,也有可能不在后续版本中提供了。
JVM的JIT编译模式相关的选项:
- -Xint表示禁用JIT,所有字节码都被解释执行,这个模式的速度最慢的。
- -Xcomp表示所有字节码都首先被编译成本地代码,然后再执行。
- -Xmixed,默认模式,让JIT根据程序运行的情况,有选择地将某些代码编译成本地代码(我们上面执行java -version看到的也是这种默认模式)。
解释执行和编译执行对不同的程序可能有不同的结果,推荐用默认模式。
C:\Users\Administrator>java -Xint -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, interpreted mode)
C:\Users\Administrator>java -Xcomp -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, compiled mode)
C:\Users\Administrator>java -Xmixed -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
-Xmx -Xms -Xss属于 XX 参数
-Xms<size> 设置初始 Java 堆大小,等价于-XX:InitialHeapSize
-Xmx<size> 设置最大 Java 堆大小,等价于-XX:MaxHeapSize
-Xss<size> 设置 Java 线程堆栈大小,-XX:ThreadStackSize
-XX参数选项
这类选项是属于实验性,主要是给JVM开发者用于开发和调试JVM的,在后续的版本中行为有可能会变化。-XX选项根据是否是boolean类型分为如下两种:
- Boolean类型格式:-XX:+<name>和-XX:-<name>分别表示启用和禁用 name 属性,例如-XX:+UseG1GC表示启用G1收集器
- 非Boolean类型格式:非Boolean类型中还分为数值型和非数值型。数值型格式-XX:<option>=<number>,number表示数值,number可以带上单位,比如:'m' / 'M' 表示兆, 'k' /'K' 表示Kb, 'g' / 'G' 表示 g(例如 32k跟32768是一样的效果),例如-XX:NewSize=1024m表示设置新生代初始大小为1024兆。非数值型格式-XX:<name>=<string>,例如:-XX:HeapDumpPath=/usr/local/heapdump.hprof,用来指定heap转存文件的存储路径。
JVM参数选项如何设置
1,开发的时候可以通过eclipse或者Idea工具进行设置。eclipse中配置如下:
2,运行jar包时可以添加JVM参数,java -Xms50m -Xmx50m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -jar demo.jar,
3,通过Tomcat运行war包时,Linux系统下可以在tomcat/bin/catalina.sh中添加类似如下配置
JAVA_OPTS="-Xms512M -Xmx1024M"
Windows系统下在catalina.bat中添加类似如下配置
set "JAVA_OPTS=-Xms512M -Xmx1024M"
4,程序运行过程中也可以修改,可以使用jinfo -flag <name>=<value> <pid>设置非Boolean类型参数,可以使用 jinfo -flag [+|-]<name>=<value> <pid>设置Boolean类型参数,pid是进程id。
下面我们介绍一下常用的参数选项。
打印设置的XX选项及值
有三个选项:-XX:+PrintCommandLineFlags、-XX:+PrintFlagsInitial、-XX:+PrintFlagsFinal
- -XX:+PrintCommandLineFlags可以让在程序运行前打印出用户手动设置或者JVM自动设置的XX选项
- -XX:+PrintFlagsInitial表示打印出所有XX选项的默认值
- -XX:+PrintFlagsFinal表示打印出XX选项在运行程序时生效的值
通过执行java -XX:+PrintFlagsFinal -version可以看到如下的参数(太多了没有贴全),我们可以看到UseParNewGC= false,UseParallelGC := true ,输出结果中 = 表示默认值,:= 表示被用户或 JVM 修改后的值
java -XX:+PrintFlagsFinal -version
[Global flags]
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
......
bool UseParNewGC = false {product}
bool UseParallelGC := true {product}
bool UseParallelOldGC = true {product}
......
uintx YoungPLABSize = 4096 {product}
bool ZeroTLAB = false {product}
intx hashCode = 5 {product}
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
另外,也可以通过 jinfo -flag XX选项名称 <pid>来进行查看运行时的JVM参数,pid是指进程id,可以通过ps -ef|grep java或者jps(jps,jinfo等jdk自带命令下节会介绍)查看,例如下面这样:
C:\Users\Administrator>jps
16084 Bootstrap
11496
18184 Jps
10636
15916
C:\Users\Administrator>jinfo -flag UseParallelGC 15916
-XX:+UseParallelGC
堆、栈、方法区等内存大小设置
方法区
方法区存放类信息+普通常量+静态常量+编译器编译后的代码等,如果程序中加载的class数量非常多,就需要增加方法区占用内存。
永久代(Java8之前,Java8中没有永久代概念,下面参数无效)
- -XX:PermSize=256m:设置永久代初始值为256M。
- -XX:MaxPermSize=256m:设置永久代最大值为256M。
元空间Metaspace(Java8中才有,使用的本地内存)
- -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集器进行类卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
- -XX:MaxMetaspaceSize,最大空间,默认没有限制。
- -XX:+UseCompressedOops压缩对象指针。 "oops"指的是普通对象指针("ordinary" object pointers),Java堆中对象指针会被压缩成32位。
- -XX:+UseCompressedClassPointers选项来压缩类指针。对象中指向类元数据的指针(对象头中的class pointer)会被压缩成32位
- -XX:CompressedClassSpaceSize参数作用是设置Klass Metaspace的大小,默认1G。Klass Metaspace就是用来存klass的,klass是的class文件在jvm里的运行时数据结构。只有当-XX:+UseCompressedClassPointers开启了才有效,但是jdk1.8里是默认开启的,如果这块内存会如果没有满会一直增加。但是如果-Xmx超过了32G,压缩指针是默认不开启的,而这个参数也就失去了设置的意义。
如果开启了-XX:+UseCompressedOops及-XX:+UseCompressedClassesPointers,则UseCompressedOops会使用32-bit的offset来代表java object的引用,而UseCompressedClassPointers则使用32-bit的offset来代表64-bit进程中的class pointer;可以使用CompressedClassSpaceSize来设置这块的空间大小。如果开启了指针压缩,则CompressedClassSpace分配在MaxMetaspaceSize里头,即MaxMetaspaceSize=Compressed Class Space Size + Metaspace area (excluding the Compressed Class Space) Size
栈
-Xss128k:设置每个线程的栈大小为128k。线程栈的大小会影响到递归调用的深度,同时也会影响到能同时开启的线程数量。在相同物理内存下,减小这个值能生成更多的线程。当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
查看默认的线程栈大小
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
intx CompilerThreadStackSize = 0 {pd product}
intx ThreadStackSize = 1024 {pd product}
intx VMThreadStackSize = 1024 {pd product}
堆内存
关于堆内存中GC过程,MinorGC,MajorGC,FullGC的区别,《内存分配与回收策略及MinorGC、MajorGC、FullGC介绍》已经介绍过了。
-Xmx3550m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为3550M。
-Xms3550m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。官方推荐配置为整个堆大小的3/8。
-XX:NewSize=1024m:设置年轻代初始值为1024M。
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
-XX:SurvivorRatio=8:设置年轻代中Eden区与一个Survivor区的比值,默认为8。
-XX:NewRatio=4:设置老年代与年轻代(包括1个Eden和2个Survivor区)的比值。表示老年代是年轻代的4倍。
-XX:PretenureSizeThreadshold=1024:设置让大于此阈值的对象直接分配在老年代(只对Serial、ParNew收集器有效),单位为字节
-XX:MaxTenuringThreshold=15:默认15,新生代每次MinorGC后,还存活的对象年龄+1,当对象的年龄大于设置的这个值时就进入老年代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。
-XX:+PrintTenuringDistribution:让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布。
-XX:TargetSurvivorRatio 表示MinorGC结束后Survivor区域中占用空间的期望比例。
关于MinorGC有如下两点注意:
- 1、如果Survivor中对象的年龄分布显示很多对象在经历了多次GC最终年龄达到了-XX:MaxTenuringThreshold才被移到老年代,这可能说明-XX:MaxTenuringThreshold设置得过大,也有可能是Survivor的空间过大。
- 2、如果每次GC后Survivor中对象的大小都没有超过期望的Survivor空间大小,则说明GC工作得很好。反之,则说明可能Survivor空间小了,使得新生成的对象很快就被移到了老年代了。
直接内存
-XX:MaxDirectMemorySize:指定DirectMemory容量,若未指定,则默认与Java堆最大值一样。
OutofMemory相关的选项
如果程序发生了OOM后,可以把内存给dump下来以分析原因,或者自动采取一些别的动作。
- -XX:+HeapDumpOnOutOfMemoryError 表示在内存出现OOM的时候,把Heap转存(Dump)到文件以便后续分析,文件名通常是java_pid<pid>.hprof,其中pid为该程序的进程号。
- -XX:HeapDumpPath=<path> 用来指定heap转存文件的存储路径
- -XX:OnOutOfMemoryError 用来指定一个可行性程序或者脚本的路径,当发生OOM的时候,去执行这个脚本。
下面的命令可以使得在发生OOM的时候,Heap被转存到文件/tmp/heapdump.hprof,同时执行Home目录中的cleanup.sh文件:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp
垃圾收集器相关选项
关于常用垃圾收集器的参数太多了,请根据自己选用的垃圾收集器选用对应的参数,可以查找开头给的官方文档地址,也可以参考我的上篇博客《常用垃圾收集器介绍》
GC日志相关选项
- -verbose:gc,输出gc日志信息,默认输出到标准输出
-
-XX:+PrintGC,等同于-verbose:gc 表示打开简化的GC日志
-
-XX:+PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配情况。
-
-XX:+PrintGCTimeStamps 打印GC发生时相对于JVM启动时的时间。
-
-XX:+PrintGCDateStamps 打印出GC发生的具体时间。
比如,以下是-XX:+PrintGCTimeStamps的输出
0,185: [GC 66048K->53077K(251392K), 0,0977580 secs]
0,323: [GC 119125K->114661K(317440K), 0,1448850 secs]
0,603: [GC 246757K->243133K(375296K), 0,2860800 secs]
以下是两个都打开后的输出:
2014-12-26T17:52:38.613-0800: 3.395: [GC 139776K->58339K(506816K), 0.1442900 secs]
- -XX:+PrintHeapAtGC 每一次GC前和GC后,都打印堆信息。
- -XX:+TraceClassLoading 监控类的加载
- -XX:+PrintGCApplicationStoppedTime 打印GC时线程的停顿时间
- -XX:+PrintGCApplicationConcurrentTime,垃圾收集之前打印出应用未中断的执行时间
- -XX:+PrintTenuringDistribution:让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布。
- -Xloggc:<file> 表示把GC日志写入到一个文件中去,而不是打印到标准输出中。
下面是关于GC日志文件控制相关的参数,设置gc文件滚动和设置滚动日志文件的个数,为了防止单个gc日志文件太大,生产上建议加上
- -XX:+UseGCLogFileRotation 启用GC日志文件的自动转储
- -XX:NumberOfGClogFiles=1 GC日志文件的循环数目
- -XX:GCLogFileSize=1M 控制GC日志文件的大小
其他参数
- -XX:+DisableExplicitGC,禁止hotspot执行System.gc(),默认禁用;
-
-XX:ReservedCodeCacheSize=<n>[g|m|k]、-XX:InitialCodeCacheSize=<n>[g|m|k],指定代码缓存的大小,用于保存已编译方法生成的本地代码,如果代码缓存被占满JVM会发出警告信息,并切换到interpreted-only模式,JIT编译器被停用,字节码将不会再编译成机器码。这样的话对JVM的性能影响很大;
-
-XX:+UseCodeCacheFlushing,如果代码缓存不断增长导致代码缓存空间不够,使用该参数让jvm放弃一些被编译的代码,避免代码缓存被占满时JVM切换到interpreted-only的情况;
-
-XX:+DoEscapeAnalysis,开启逃逸分析,逃逸分析是一种分析对象范围的技术,在一些情况一个线程分配的对象可能会被其他对象使用,这种现象叫做“逃逸”,如果一个对象没有“逃逸”,则可以运用一些额外的优化技术,这种优化技术“逃逸分析”。通过“逃逸分析” JIT可以使用如下技术优化:
- 栈上分配
- 消灭同步
- 消灭垃圾回收读写障碍
- 对象爆炸
-
-XX:+UseBiasedLocking,开启偏向锁,偏向锁是是锁偏爱上次使用它的线程,在非竞争锁的场景下,可以实现无锁的开销。
-
-XX:+UseLargePages,开启使用大页面,使用大页面可以提高TLB(translation lookaside buffer(转换后备缓存区))的缓存命中率;
通过Java代码获取JVM参数
Java提供了java.lang.management包用于监视和管理Java虚拟机和Java运行时中的其他组件,它允许本地和远程监控和管理运行的Java虚拟机。其中ManagementFactory这个类还是挺常用的。另外还有Runtime类也可以获取一些内存、CPU核数等相关的数据。可以通过这些api可以监控我们的应用服务器的堆内存使用情况,设置一些阈值进行报警等处理。
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class MemoryMonitor {
public static void main(String[] args) {
MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
MemoryUsage usage = memorymbean.getHeapMemoryUsage();
System.out.println("INIT HEAP: " + usage.getInit() / 1024 / 1024);
System.out.println("MAX HEAP: " + usage.getMax() / 1024 / 1024);
System.out.println("USE HEAP: " + usage.getUsed() / 1024 / 1024);
System.out.println("\nFull Information:");
System.out.println("Heap Memory Usage: " + memorymbean.getHeapMemoryUsage());
System.out.println("Non-Heap Memory Usage: " + memorymbean.getNonHeapMemoryUsage());
System.out.println("=======================通过java来获取相关系统状态============================ ");
System.out.println("当前堆内存大小totalMemory " + (int) Runtime.getRuntime().totalMemory() / 1024 / 1024 + " M");// 当前堆内存大小
System.out.println("空闲堆内存大小freeMemory " + (int) Runtime.getRuntime().freeMemory() / 1024 / 1024 + " M");// 空闲堆内存大小
System.out.println("最大可用总堆内存maxMemory " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + " M");// 最大可用总堆内存大小
}
}
输出如下:
INIT HEAP: 252
MAX HEAP: 3575
USE HEAP: 2
Full Information:
Heap Memory Usage: init = 264241152(258048K) used = 2642440(2580K) committed = 253231104(247296K) max = 3749183488(3661312K)
Non-Heap Memory Usage: init = 2555904(2496K) used = 4229480(4130K) committed = 8060928(7872K) max = -1(-1K)
=======================通过java来获取相关系统状态============================
当前堆内存大小totalMemory 241 M
空闲堆内存大小freeMemory 238 M
最大可用总堆内存maxMemory 3575 M