Java
Java虚拟机

Java虚拟机08 - 常用JVM参数

简介:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。

1. JVM参数类型

JVM参数大体可以分为标准参数和非标准参数。

1.1. 标准参数

标准参数保证在所有的JVM上都实现。不是以-X-XX开头的参数属于标准参数,例如-Dproperty=value -jar filename -client等。

1.2. 非标准参数

非标准参数,不保证在所有的JVM上都实现,非标准参数分为以-X开头的和以-XX开头的。

  1. -X开头的参数是非标准参数,有可能不在Release Note中通知就改变,如:
  • Xint:完全解释执行。
  • Xcomp:第一次使用就编译成本地代码。
  • Xmixed:混合模式,JVM自己来决定是否编译成本地代码。

上面的三个参数测试如下:

  • $ > java -version
  • java version "1.8.0_144"
  • Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
  • Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
  • D:\WorkSpace
  • $ > java -Xint -version
  • java version "1.8.0_144"
  • Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
  • Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, interpreted mode)
  • D:\WorkSpace
  • $ > java -Xcomp -version
  • java version "1.8.0_144"
  • Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
  • Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, compiled mode)

从最后一行打印的mixed modeinterpreted modecompiled mode可以得知-X参数起作用了,同时JVM默认是以混合模式运行的。

注:HotSpot虚拟机中内置了两个即时编译器,分别称为Client Compiler和Server Compiler,或者简称为C1编译器和C2编译器(也叫Opto编译器)。目前主流的HotSpot虚拟机(Sun系列JDK 1.7及之前版本的虚拟机)中,默认采用解释器与其中一个编译器直接配合的方式工作,程序使用哪个编译器,取决于虚拟机运行的模式,HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用-client-server参数去强制指定虚拟机运行在Client模式或Server模式。无论采用的编译器是Client Compiler还是Server Compiler,解释器与编译器搭配使用的方式在虚拟机中称为“混合模式”(Mixed Mode),用户可以使用参数-Xint强制虚拟机运行于“解释模式”(Interpreted Mode),这时编译器完全不介入工作,全部代码都使用解释方式执行。另外,也可以使用参数-Xcomp强制虚拟机运行于“编译模式”(Compiled Mode),这时将优先采用“编译方式执行程序,但是解释器仍然要在编译无法进行的情况下介入执行过程,可以通过虚拟机的-version命令的输出结果显示出这3种模式。

  1. -XX开头的参数是不稳定的,可能不通知就改变。-XX参数有以下使用规则:
  • 对于boolean类型,-XX:+<选项>表示打开选项;-XX:-<选项>表示关闭掉选项。
  • 对于数值类型,可用-XX:<选项>=<值>表示;值默认单位是字节,也可用单位有k或者Km或者Mg或者G,例如32k表示32768
  • 对于String类型,可用-XX:<选项>=<值>,通常用来指定一个文件、路径或者命令。

注:-Xmx和-Xms其实是-XX参数,它们分别等价于-XX:initialHeapSize-XX:MaxHeapSize

2. 查看JVM运行时参数

查看JVM运行参数的方式有以下的几种:

  1. 使用-XX:+PrintFlagsInitial查看初始值
  • ubuntu@s100:~$ java -XX:+PrintFlagsFinal -version
  • [Global flags]
  • uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
  • uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
  • ...
  • uintx InitialHeapSize = 0 {product}
  • ...
  • bool UseG1GC = false {product}
  • bool UseGCLogFileRotation = false {product}
  1. 使用-XX:+PrintFlagsFinal查看最终的值

-XX:+PrintFlagsFinal可以查看某个参数值是否已经配置。该方法在JDK7与JDK8,甚至JDK7中的不同小版本,有些参数值都不一样,因此要以生产环境同版本的JDK打出来的为准。

可以使用类似-version的方式在不启动应用的情况下查看参数:

  • ubuntu@s100:~$ java -XX:+PrintFlagsFinal -version
  • [Global flags]
  • uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
  • uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
  • ...
  • uintx InitialHeapSize := 65011712 {product}
  • ...
  • bool UseG1GC = false {product}
  • bool UseGCLogFileRotation = false {product}
  • ...

注:在查询参数的值时,第二列的信息表示中,= number表示默认值,:= number表示被用户或者JVM修改后的值。

有些参数设置后会影响其他参数,所以也要带上:

  • $ > java -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads
  • uintx ParallelGCThreads = 4 {product}
  • java version "1.6.0_65"
  • Java(TM) SE Runtime Environment (build 1.6.0_65-b14-468)
  • Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-468, mixed mode)

下面的语句可以查看当前JDK一共有多少个参数:

  • $ > java -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -version | wc -l
  • java version "1.6.0_65"
  • Java(TM) SE Runtime Environment (build 1.6.0_65-b14-468)
  • Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-468, mixed mode)
  • 756
  1. 使用-XX:+unlockExperimentalVMOptions解锁实验参数
  2. 使用-XX:+UnlockDiagnosticVMOptions解锁诊断参数
  3. 使用-XX:+PrintCommandLineFlags打印命令行参数
  4. 使用jinfo查看特定参数

使用jinfo命令加上-flag参数可以查看某个应用的某个参数当前的值,例如下面的例子查看了Tomcat的MaxHeapSize参数的值:

  • $ > jps -l
  • 20052 sun.tools.jps.Jps
  • 2168 org.apache.catalina.startup.Bootstrap
  • $ > jinfo -flag MaxHeapSize 2168
  • -XX:MaxHeapSize=4284481536

jinfo命令加上-flags参数还可以将所有已经赋过值的所有参数打印出来:

  • $ > jinfo -flags 2168
  • Attaching to process ID 2168, please wait...
  • Debugger attached successfully.
  • Server compiler detected.
  • JVM version is 25.144-b01
  • Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4284481536 -XX:MaxNewSize=1428160512 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
  • Command line: -Djava.util.logging.config.file=C:\Java\apache-tomcat-7.0.52\conf\logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=C:\Java\apache-tomcat-7.0.52\endorsed -Dcatalina.base=C:\Java\apache-tomcat-7.0.52 -Dcatalina.home=C:\Java\apache-tomcat-7.0.52 -Djava.io.tmpdir=C:\Java\apache-tomcat-7.0.52\temp

3. 常用参数

参数名称 含义 默认值 备注
-Xms 初始堆大小。 物理内存的1/64(< 1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx 最大堆大小。 物理内存的1/4(< 1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
-Xmn 年轻代大小(1.4+) 注意:此处的大小是(Eden + 2 Survivor Space)。与jmap -heap中显示的New Gen是不同的。整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小。增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:NewSize 年轻代大小(1.3/1.4)。
-XX:MaxNewSize 年轻代最大值(1.3/1.4)。
-XX:PermSize 持久代(Permanent Generation)初始值。 物理内存的1/64 方法区也被称作持久代。
-XX:MaxPermSize 持久代最大值。 物理内存的1/4 方法区也被称作持久代。
-Xss 每个线程分配的内存大小。 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用,如果栈不是很深,应该是128K够用的 大的应用建议使用256K。这个选项对性能影响比较大,需要严格的测试。(校长)和ThreadStackSize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:“-Xss is translated in a VM flag named ThreadStackSize”,一般设置这个值就可以了。
-XX:ThreadStackSize Thread Stack Size (0 means use default stack size)[Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。 -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5,Xms等于Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值。 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
-XX:LargePageSizeInBytes 内存页的大小不可设置过大,会影响Perm的大小。 =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化。
-XX:+DisableExplicitGC 关闭System.gc()
-XX:MaxTenuringThreshold 垃圾最大年龄。 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。该参数只有在串行GC时才有效。
-XX:+AggressiveOpts 加快编译。
-XX:+UseBiasedLocking 锁机制的性能改善。
-Xnoclassgc 禁用垃圾回收。
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间。 1s softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配。 0 单位为字节,新生代采用Parallel Scavenge GC时无效,另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象。
-XX:TLABWasteTargetPercent TLAB占Eden区的百分比。 1%
-XX:+CollectGen0First FullGC时是否先Young GC。 false

4. 并行收集器相关参数

参数 含义 默认值 备注
-XX:+UseParallelGC Full GC采用parallel MSC。 选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:+UseParNewGC 设置年轻代为并行收集。 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
-XX:ParallelGCThreads 并行收集器的线程数。 此值最好配置与处理器数目相等,同样适用于CMS。
-XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(Parallel Compacting)。 JAVA 6出现的参数选项。
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间)。 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例。 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比。 公式为1/(1+n)。
-XX:+ScavengeBeforeFullGC Full GC前调用YGC。 true Do young generation GC prior to a full GC.(Introduced in 1.4.1.)

5. CMS相关参数

参数 含义 默认值 备注
-XX:+UseConcMarkSweepGC 使用CMS内存收集。 测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
-XX:+AggressiveHeap 试图是使用大量的物理内存 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要256MB内存 大量的CPU/内存。
-XX:CMSFullGCsBeforeCompaction 多少次后进行内存压缩。 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+CMSParallelRemarkEnabled 降低标记停顿。
-XX+UseCMSCompactAtFullCollection 在Full GC的时候,对年老代的压缩。 CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始CMS收集。 禁止HostSpot自行触发CMS GC。
-XX:CMSInitiatingOccupancyFraction=70 使用CMS作为垃圾回收,使用70%后开始CMS收集。 92 为了保证不出现Promotion Failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式。
-XX:CMSInitiatingPermOccupancyFraction 设置Perm Gen使用到达多少比率时触发。 92
-XX:+CMSIncrementalMode 设置为增量模式。 用于单CPU情况。
-XX:+CMSClassUnloadingEnabled

6. 辅助信息

参数 含义 默认值 备注
-XX:+PrintGC 打印GC日志。
-XX:+PrintGCDetails 打印详细的GC日志。
-XX:+PrintGCTimeStamps
-XX:+PrintGC:PrintGCTimeStamps 可与-XX:+PrintGC-XX:+PrintGCDetails混合使用。
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用 输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间。
-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息。
-Xloggc:filename 把相关日志信息记录到文件以便分析。与上面几个配合使用。
-XX:+PrintClassHistogram garbage collects before printing the histogram.
-XX:+PrintTLAB 查看TLAB空间的使用情况。
-XX:+PrintTenuringDistribution 查看每次Minor GC后新的存活周期的阈值。 Desired survivor size 1048576 bytes, new threshold 7 (max 15) new threshold 7即标识新的存活周期的阈值为7。

7. 堆大小设置

JVM中最大堆大小有三方面限制:

  1. 相关操作系统的数据模型(32位还是64位)限制。32位系统下,一般限制在1.5G ~ 2G;64为操作系统对内存无限制。
  2. 系统的可用虚拟内存限制。
  3. 系统的可用物理内存限制。

【典型配置一】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k

  • -Xmx8192m:设置JVM最大可用堆内存为8192M。
  • -Xms8192m:设置JVM初始堆内存为8192M。此值可以设置与-Xmx相同,可以以避免每次垃圾回收完成后JVM重新分配内存。
  • -Xmn2g:设置年轻代大小为2G。整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64M,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
  • -Xss128k:设置每个线程的堆栈大小。JDK 5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。需根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000 ~ 5000左右。

【典型配置二】:java -Xmx8192m -Xms8192m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

  • -XX:NewRatio=4:设置年老代(除去持久代)与年轻代(包括Eden和两个Survivor区)的比值。设置为4,则年老代与年轻代所占比值为4:1,年轻代占整个堆栈的1/5。
  • -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则Eden区域一个Survivor区的大小比值为4:1,Eden区与两个Survivor区的比值为2:4,一个Survivor区占整个年轻代的1/6。
  • -XX:MaxPermSize=16m:设置持久代大小为16M。
  • -XX:MaxTenuringThreshold=0:设置对象进入老年代所需要经历的GC次数。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代对象比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。

8. 收集器的选择

JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK 5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

8.1. 组合的选择

  • 单CPU或小内存,单机程序:-XX:+UseSerialGC
  • 多CPU,需要最大吞吐量,如后台计算型应用:-XX:+UseParallelGC-XX:UseParallelOldGC
  • 多CPU,追求低停顿时间,需快速响应,如互联网应用:-XX:+UseConcMarkSweepGC-XX:+ParNewGC
参数 新生代 新生代算法 老年代 老年代算法
-XX:+UseSerialGC Serial 复制 SerialOld 标记整理
-XX:+UseParNewGC ParNew 复制 SerialOld 标记整理
-XX:+UseParallelGC-XX:+UseParallelOldGC Parallel Scavenge 复制 ParallelOld 标记整理
-XX:+UseConcMarkSweepGC ParNew 复制 CMS + Serial Old备选 标记清除
-XX:+UseG1GC G1 局部使用复制算法 局部使用复制算法,不会产生内存碎片

8.2. 串行

【Serial】:新生代串行收集器,使用复制算法。

-XX:+UseSerialGC:新生代、老年代使用串行收集器,新生代使用复制算法,老年代使用标记-整理算法。

【Serial Old】:Serial收集器的老年代版本,使用标记整理算法。

8.3. 并行

【ParNew】:新生代并行收集器,Serial的并行版本,使用复制算法。Server模式下的除了Serial收集器外,唯一可与CMS收集器配合工作的并行收集器。可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。开启ParNew收集器可以使用以下参数:

  • -XX:+UseParNewGC:新生代使用ParNew收集器,老年代使用串行收集器。
  • -XX:+UseConcMarkSweepGC:新生代使用ParNew收集器,老年代使用CMS收集器。
  • -XX:ParallelGCThreads:限制线程数量。

【Parallel Scavenge】:新生代并行收集器,使用复制算法。关注可控制的吞吐量(Throughput)。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。一旦新生代使用了Parallel Scavenge,老年代只能使用Serial Old或Parallel Old。

  • -XX:MaxGCPauseMillis:设置使用Parallel Scavenge收集器时的最大暂停时间。
  • -XX:GCTimeRatio:设置使用Parallel Scavenge收集器时的吞吐量大小,(0, 100)之间的整数,表示垃圾收集时间占总时间的比率。
  • -XX:PretenureSizeThreshold:晋升老年代对象年龄。

【Parallel Old】:Parallel Scavenge收集器的老年代版本,使用标记整理算法。更加关注吞吐量。

  • -XX:+UseParallelGC:新生代使用Parallel收集器,老年代使用串行收集器。
  • -XX:+UseParallelOldGC:新生代使用Parallel Scavenge收集器,老年代使用Parallel Old收集器。
  • -XX:ParallelGCThreads:可以设置垃圾回收时的线程数量。
  • -XX:MaxGCPauseMills:最大停顿时间,单位毫秒,GC尽力保证回收时间不超过设定值
  • -XX:GCTimeRatio:调节吞吐量。0-100的取值范围,垃圾收集时间占总时间的比,默认99,即最大允许1%时间做GC。
  • -XX:MaxGCPauseMills-XX:GCTimeRatio是矛盾的,因为停顿时间和吞吐量不可能同时调优。

8.4. CMS

CMS(Concurrent Mark Sweep):以获取最短回收停顿时间为目标的收集器,基于标记清除算法实现的,标记清除算法相对于标记整理算法停顿更短,但更容易产生内存碎片。细分为6个步骤:

  • 初始标记(CMS initial mark):需要“Stop The World”,标记一下GC Roots能直接关联到的对象,速度很快。
  • 并发标记(CMS concurrent mark):进行GC Roots Tracing的过程,根据初始标记阶段发现的所有GC Roots所关联的对象扫描整个老年代,标记所有存活对象。
  • 预清理(CMS pre clean):用于清理前准备和检查,并根据历史性能数据来决定控制停顿时间进行重新标记。由于接下来的重新标记是需要“stop the world”的,如果新生代GC发生后立即出发一次重新标记可能会导致停顿时间很长,为了避免这种情况,预处理阶段会可以等待一次新生代GC的发生,然后根据历史性能数据预测下一次新生代GC可能发生的时间,然后在当前时间和预测时间的中间时刻,进行重写标记,从最大程度避免新生代GC和重新标记的时间重合导致大量停顿时长。
  • 重新标记(CMS remark):需要“Stop The World”,修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
  • 并发清除(CMS concurrent sweep):清理垃圾。
  • 并发重置(CMS concurrent reset):垃圾回收完成后,重新初始化CMS数据和相应的数据结构。

缺点:

  1. 在CPU核数较少时对CPU资源非常敏感。
  2. 无法处理浮动垃圾。
  3. 基于标记清除算法,会有大量空间碎片产生。

当老年代使用了68%(1.5默认68%,1.6+为92%)的空间后就会被激活,因为和用户线程一起运行,不能在空间快满时再清理,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比。如果不幸内存预留空间不够,就会出现“Concurrent Mode Failure”失败,将临时启用Serial Old收集器重新进行老年代的垃圾收集。

  • -XX:+UseConcMarkSweepGC:老年代使用CMS收集器,新生代使用ParNew。
  • -XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发。
  • -XX:+ UseCMSCompactAtFullCollection:CMS Full GC后,进行一次整理,整理过程是独占的,会引起停顿时间变长。
  • -XX:+CMSFullGCsBeforeCompaction:设置进行几次CMS Full GC后,进行一次碎片整理。
  • -XX:ParallelCMSThreads:设定CMS的收集线程数量。
  • -XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收。
  • -XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收。
  • -XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收。

8.5. G1

G1具备如下特点:

  • 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间。
  • 分代收集:分代概念在G1中依然保留。G1可以独立管理整个GC堆,且采用不同的方式去处理分代对象。
  • 空间整合:G1从整体来看是基于标记整理算法实现的,从局部(两个Region之间)上来看是基于复制算法实现的;G1收集后能提供规整的可用内存。
  • 可预测的停顿:G1能建立可预测的停顿时间模型,能明确指定垃圾收集相对于时间段的吞吐量。
  • 相对于CMS:几乎不会产生内存碎片,且可以精确控制GC停顿时间。

G1收集器将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

G1根据各个Region回收所获得的空间大小以及回收所需时间等指标在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而可以有计划地避免在整个Java堆中进行全区域的垃圾收集。

  • -XX:+UseG1GC:用于打开G1收集器。
  • -XX:G1HeapRegionSize=n:设置G1区域的大小。值是2的幂,范围是1M到32M。目标是根据最小的Java堆大小划分出约2048个区域。
  • -XX:MaxGCPauseMillis:用于指定目标最大停顿时间。如果任何一次停顿超过这个设置值时,G1会尝试调整新生代和老年代的比例、堆大小、晋升年龄手段,以试图达到这个预设目标。但如果停顿时间过短,必然会增加新生代的GC频率,更有可能使Full GC触发的次数增加。
  • -XX:InitiatingHeapOccupancyPercent:用于指定当整个堆使用率达到多少时,触发并发标记周期的执行,默认是45,即当整个堆使用率达到45%时会触发并行标记周期。G1始终不会自己修改这个值。如果该值偏大,会导致因并发标记周期过少而引起Full GC操作,过小会导致并发标记周期过于频繁,从而抢占用户线程资源。
  • -XX:ConcGCThreads=n:并发GC使用的线程数。
  • -XX:G1ReservePercent=n:设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,默认值是10%。

8.5.1. TLAB

G1 GC会默认会启用TLAB优化。其作用就是在并发情况下,基于CAS的独享线程(Mutator Threads)可以优先将对象分配在一块内存区域(属于Java堆的Eden中),因为是Java线程独享的内存区,没有锁竞争,所以分配速度更快,每个TLAB都是一个线程独享的。如果待分配的对象被判断是巨型对象,则不使用TLAB。

8.5.2. PLAB

Promotion Local Allocation Buffer,即晋升本地分配缓冲区。在新生代GC中,对象会将全部Eden区存活的对象转移(复制)到Survivor区。也会存在Survivor区对象晋升(Promotion)到老年代。这个决定晋升的阀值可以通过-XX:MaxTenuringThreshold设定,默认15次。晋升的过程,无论是晋升到Survivor区还是Old区,都是在GC线程的PLAB中进行。每个GC线程都有一个PLAB。

8.5.3. Collection Sets

简称CSets,待收集集合。GC中待回收的Region的集合。CSet中可能存放着各个分代的Region,对于新生代GC,CSet只包含新生代Region,对于Mixed GC,CSet包含新生代Region和老年代Region。CSet中的存活对象会在GC过程中被移动(复制),GC后CSet中的Region会成为可用分区。

8.5.4. Card Table

Card Table用于在对新生代做GC Root枚举时避免对老年代进行全扫描。Java虚拟机将Java堆划分为相等大小的一个个区域,这个小的区域(一般大小在128 ~ 512字节)被称做Card,而Card Table维护着所有的Card。Card Table的结构是一个字节数组,Card Table用单字节的信息映射着一个Card。当Card中存储了对象时,称为这个Card被脏化了(Dirty Card)。对于一些热点Card会存放到Hot Card Cache中。同Card Table一样,Hot Card Cache也是全局的结构。

8.5.5. Remembered Sets

与Card Table类似。

只想收集新生代(换句话说,不想收集老年代),所以没有必要对位于老年代的 GC Roots 做全面的可达性分析。但问题是,确实可能存在位于老年代的某个 GC Root,它引用了新生代的某个对象,这个对象你是不能清除的。“老年代对象引用新生代对象”这种关系,会在引用关系发生时,在新生代边上专门开辟一块空间记录下来,这就是RememberedSet。

8.5.6. STAB

Snapchat At The Beginning:

三色标记算法,The Garbage Collection Handbook中将在对象存在定义为三种状态:

  • 白:对象没有被标记到,标记阶段结束后,会被当做垃圾回收掉。
  • 灰:对象被标记了,但是它的field还没有被标记或标记完。
  • 黑:对象被标记了,且它的所有field也被标记完了。

由于并发阶段的存在,Mutator和Garbage Collector线程同时对对象进行修改,就会出现白对象漏标的情况(漏标的情况只会发生在白色对象中),这种情况发生的前提是:

  1. Mutator赋予一个黑对象该白对象的引用。
  2. Mutator删除了所有从灰对象到该白对象的直接或者间接引用。

解决新创建对象产生的漏标问题:并发标记期间新分配的对象都会被打上标记,不会被清理。
解决对象引用被修改产生的漏标问题:利用pre-write barrier,将所有即将被修改引用关系的白对象旧引用记录下来,最后以这些旧引用为根重新扫描一遍,以解决白对象引用被修改产生的漏标问题。

8.5.7. G1运作过程

分为四大阶段:

  1. 新生代GC:被称作疏散(Evacuation)阶段,G1收集器在这个阶段的操作其实和其他收集器是类似的,即收集Eden区的数据到Survivor区,并清理无用区域。
  2. 并发标记周期。分为以下几个阶段:

    • 初始标记(Initial Marking):需要“Stop the World”。这个阶段用于标记GC Root直接可达对象,一定会有一次新生代GC。
    • 根区域扫描(Root Region Scan):可与用户线程并发执行。初始标记阶段的新生代GC会将Eden清空,存活对象被转入Survivor区,所以在本阶段将扫描并标记由Survivor区直接可达的老年代区域。由于依赖Survivor的当前状况,此阶段不能和新生代GC同时执行。
    • 并发标记(Concurrent Marking):可与用户线程并发执行。扫描、查找并标记整个堆的存活对象,该过程可以被一次新生代GC打断,也有一定可能直接被取消,如果在这一步被取消,整个并发标记周期会放弃重来。
    • 重新标记(Remarking):需要“Stop the World”。对并发标记的结果进行修正和补充。
    • 独占清理(Cleanup):需要“Stop the World”。根据优先列表进行相应Region的垃圾回收,更新Remembered Set。
    • 并发清理(Concurrent Clean):可与用户线程并发执行。识别并清理完全空闲的区域。
  3. 混合回收周期。混合回收即Mixed GC,之所以称之为混合回收,是因为它同时对新生代和老年代同时进行GC操作。在并发标记周期中,虽然也有部分对象被回收,但是比例非常低。在并发标记周期之后,含有较多垃圾对象的Region已经被筛选出来了,G1会在混合回收周期统一对这些Region进行垃圾回收处理。

  4. Full GC(可能会发生)。由于G1收集器的运行流程中,GC线程和用户线程是交互执行了,所以也会出现某些回收周期中出现内存不足的情况,这种情况下会触发一个Full GC。像上面提到的终止并发标记步骤的情况,就是因为内存不足导致出现了一次Full GC操作。

8.6. 吞吐量优先的并行收集器

如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

【典型配置一】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

  • -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并行收集,而年老代仍旧使用Serial Old收集。
  • -XX:ParallelGCThreads=20:配置并行收集器的线程数,即同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

【典型配置二】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

  • -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集,此配置下年轻代默认使用Parallel Scavenge收集器。JDK 6.0支持对年老代并行收集。

【典型配置三】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

  • -XX:MaxGCPauseMillis=100:设置每次垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整分代大小,以满足此值。该值仅在Parallel Scavenge收集器时生效,需谨慎使用。

【典型配置四】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

  • -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

8.7. 响应时间优先的并发收集器

如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

【典型配置一】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

  • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
  • -XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

【典型配置二】:java -Xmx8192m -Xms8192m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

  • -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。

9. 辅助信息解释

JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:

  1. -XX:+PrintGC:打印GC日志,输出形式:
  • [GC 118250K->113543K(130112K), 0.0094143 secs]
  • [Full GC 121376K->10414K(130112K), 0.0650971 secs]
  1. -XX:+PrintGCDetails:打印详细GC日志,输出形式:
  • [GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
  • [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]
  1. -XX:+PrintGCTimeStamps -XX:+PrintGC:打印GC发生的时间戳,可与上面两个混合使用,输出形式:
  • 11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
  1. -XX:+PrintGCApplicationConcurrentTime: 打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用,输出形式:
  • Application time: 0.5291524 seconds
  1. -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用,输出形式:
  • Total time for which application threads were stopped: 0.0468229 seconds
  1. -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息,输出形式:
  • 34.702: [GC {Heap before gc invocations=7:
  • def new generation total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
  • eden space 49152K, 99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
  • from space 6144K, 55% used [0x221d0000, 0x22527e10, 0x227d0000)
  • to space 6144K, 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
  • tenured generation total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
  • the space 69632K, 3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
  • compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
  • the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
  • ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
  • rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
  • 34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
  • def new generation total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
  • eden space 49152K, 0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
  • from space 6144K, 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
  • to space 6144K, 0% used [0x221d0000, 0x221d0000, 0x227d0000)
  • tenured generation total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
  • the space 69632K, 4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
  • compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
  • the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
  • ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
  • rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
  • }
  • , 0.0757599 secs]
  1. -Xloggc:filename:把相关日志信息记录到文件以便分析,可与上面几个配合使用。

10. 常见配置汇总

  1. 堆设置

    • -Xms:初始堆大小。
    • -Xmx:最大堆大小。
    • -XX:NewSize:设置年轻代大小。
    • -XX:NewRatio:设置年老代(除去持久代)与年轻代(包括Eden和两个Survivor区)的比值。设置为4,则年老代与年轻代所占比值为4:1,年轻代占整个堆栈的1/5。
    • -XX:SurvivorRatio:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则Eden区域一个Survivor区的大小比值为4:1,Eden区与两个Survivor区的比值为2:4,一个Survivor区占整个年轻代的1/6。
    • -XX:MaxPermSize:设置持久代大小。
    • -XX:MaxTenuringThreshold:设置对象进入老年代所需要经历的GC次数,默认15次。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。
  2. 收集器设置

    • -XX:+UseSerialGC:设置串行收集器。
    • -XX:+UseParallelGC:设置并行收集器。
    • -XX:+UseParalledlOldGC:设置并行年老代收集器。
    • -XX:+UseConcMarkSweepGC:设置并发收集器。
  3. 垃圾回收统计信息

    • -XX:+PrintGC:打印GC日志信息。
    • -XX:+PrintGCDetails:打印详细的GC日志信息。
    • -XX:+PrintGCTimeStamps:打印GC发生的时间戳。
    • -Xloggc:filename:设置存放GC日志的文件。
  4. 并行收集器设置

    • -XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数。
    • -XX:MaxGCPauseMillis:设置使用Parallel Scavenge收集器时的最大暂停时间。
    • -XX:GCTimeRatio:设置使用Parallel Scavenge收集器时垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)。
  5. 并发收集器设置

    • -XX:+CMSIncrementalMode:设置为增量模式,适用于单CPU情况。
    • -XX:ParallelGCThreads:设置并发收集器年轻代收集方式为并行收集时使用的并行收集线程数。

11. 调优总结

年轻代大小选择:

  1. 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代发生垃圾收集的频率也是最小的,同时可减少到达年老代的对象。
  2. 吞吐量优先的应用:尽可能的设置大,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8个CPU以上的应用。

年老代大小选择:

  1. 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。因为使用传统的标记清除算法,如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

    • 并发垃圾收集信息。
    • 持久代并发收集次数。
    • 传统GC信息。
    • 花在年轻代和年老代回收上的时间比例。
  2. 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代仅存放长期存活对象。

较小堆引起的碎片问题:

因为年老代的并发收集器使用标记清除算法,所以不会对堆进行压缩。当收集器回收时,它会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现内存碎片,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记清除方式进行回收。如果出现内存碎片,可能需要进行如下配置:

  1. -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
  2. -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩。

12. Linux命令

下面列举一些常用的用于诊断系统性能的CPU命令。

12.1. CPU

  • vmstat
  • # 每2秒打印1次,打印3次
  • ubuntu@s100:~$ vmstat -n 2 3
  • # 关注r、b、us、sy
  • # r:运行和等待CPU时间片的进程数,该值最好不超过CPU总核数的2倍,过高表示压力过大。
  • # b:等待资源的进程数,如等待磁盘或网络I/O。
  • # us:用户进程消耗CPU时间百分比,长期大于50%则需要优化。
  • # sy:内核进程消耗CPU时间百分比。
  • # us + sy参考值为80%,如超过80%说明CPU不足。
  • # id:处于空闲的CPU百分比。
  • # wa:系统等待IO的CPU时间百分比。
  • # st:来自一个虚拟机偷取CPU时间的百分比。
  • procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
  • r b swpd free buff cache si so bi bo in cs us sy id wa st
  • 1 0 0 4835220 212520 756476 0 0 4 6 41 47 1 0 99 0 0
  • 0 0 0 4835204 212520 756476 0 0 0 0 213 620 0 0 100 0 0
  • 0 0 0 4835204 212520 756476 0 0 0 0 197 604 0 0 100 0 0
  • mpstat
  • # 安装
  • ubuntu@s100:~$ sudo apt install sysstat -y
  • ubuntu@s100:~$ mpstat -P ALL 2
  • Linux 4.4.0-87-generic (s100) 2019年10月17日 _x86_64_ (2 CPU)
  • 07时54分22秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
  • 07时54分24秒 all 0.50 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.50
  • 07时54分24秒 0 0.50 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.50
  • 07时54分24秒 1 0.00 0.00 0.50 0.00 0.00 0.00 0.00 0.00 0.00 99.50
  • pidstat
  • # 17871是进程PID
  • ubuntu@s100:~$ pidstat -u 1 -p 17871
  • Linux 4.4.0-87-generic (s100) 2019年10月17日 _x86_64_ (2 CPU)
  • 07时55分18秒 UID PID %usr %system %guest %CPU CPU Command
  • 07时55分19秒 1000 17871 0.00 0.00 0.00 0.00 0 java
  • 07时55分20秒 1000 17871 0.00 0.00 0.00 0.00 0 java
  • 07时55分21秒 1000 17871 0.00 0.00 0.00 0.00 0 jav

12.2. 内存

  • # 查看内存使用
  • ubuntu@s100:~$ free
  • total used free shared buff/cache available
  • Mem: 8157492 2353184 4835144 9172 969164 5504332
  • Swap: 1046524 0 1046524
  • # 以m为单位显示
  • ubuntu@s100:~$ free -m
  • total used free shared buff/cache available
  • Mem: 7966 2298 4721 8 946 5375
  • Swap: 1021 0 1021
  • # 以g为单位
  • ubuntu@s100:~$ free -g
  • total used free shared buff/cache available
  • Mem: 7 2 4 0 0 5
  • Swap: 0 0 0
  • # pidstat也可以查看内存使用量
  • ubuntu@s100:~$ pidstat -p 17871 -r 2
  • Linux 4.4.0-87-generic (s100) 2019年10月17日 _x86_64_ (2 CPU)
  • 07时57分17秒 UID PID minflt/s majflt/s VSZ RSS %MEM Command
  • 07时57分19秒 1000 17871 0.00 0.00 2807072 391272 4.80 java
  • 07时57分21秒 1000 17871 1.00 0.00 2807072 391272 4.80 java

12.3. 磁盘

  • ubuntu@s100:~$ df -h
  • 文件系统 容量 已用 可用 已用% 挂载点
  • udev 3.9G 0 3.9G 0% /dev
  • tmpfs 797M 9.0M 788M 2% /run
  • /dev/sda1 19G 8.3G 9.4G 47% /
  • tmpfs 3.9G 0 3.9G 0% /dev/shm
  • tmpfs 5.0M 0 5.0M 0% /run/lock
  • tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
  • tmpfs 797M 0 797M 0% /run/user/1000

12.4. 磁盘IO

  • # rkB:每秒读取数据量。
  • # wkB:每秒写入数据量。
  • # svctm:I/O请求的平均服务时间,单位毫秒。
  • # await:I/O请求的平均等待时间,单位毫秒。
  • # util:1秒钟有百分之几的时间用于I/O。
  • # svctm与await值接近,说明几乎没有I/O,await远高于svctm,说明I/O等待过程,可能需要优化或更换磁盘。
  • ubuntu@s100:~$ iostat -xdk 2 3
  • Linux 4.4.0-87-generic (s100) 2019年10月17日 _x86_64_ (2 CPU)
  • Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
  • sda 0.00 1.17 0.19 1.03 7.15 11.35 30.20 0.00 1.50 8.29 0.21 0.51 0.06
  • # pidstat也可以查看I/O负载
  • ubuntu@s100:~$ pidstat -d 2 -p 17871
  • Linux 4.4.0-87-generic (s100) 2019年10月17日 _x86_64_ (2 CPU)
  • 08时02分44秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
  • 08时02分46秒 1000 17871 0.00 0.00 0.00 0 java
  • 08时02分48秒 1000 17871 0.00 0.00 0.00 0 java

12.5. 网络IO

  • ubuntu@s100:~$ sudo apt install ifstat -y
  • ubuntu@s100:~$ ifstat 1
  • ens33
  • KB/s in KB/s out
  • 1.29 0.52
  • 0.66 0.34

13. CPU占用过高

下面是用于诊断CPU过高时所使用的命令:

  • # 17871是Java进程的PID,可使用jps命令获得,我们需要找出%CPU占用过高的线程TID,这里以17890为例
  • ubuntu@s100:~$ ps -mp 17871 -o THREAD,tid,time
  • USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME
  • ubuntu 0.1 - - - - - - 00:01:16
  • ubuntu 0.0 19 - futex_ - - 17871 00:00:00
  • ubuntu 0.0 19 - futex_ - - 17890 00:00:02
  • ubuntu 0.0 19 - futex_ - - 17891 00:00:00
  • # 转换17890线程ID为16进制
  • ubuntu@s100:~$ printf "%x\n" 17890
  • 45e2 # 45e2是17890的16进制
  • # 使用jstack打印线程栈,-A60表示显示10行
  • ubuntu@s100:~$ jstack 17871 | grep 45e2 -A10
  • # 注意,此处的nid=0x45e2即对应了我们要求的45e2
  • "DestroyJavaVM" #89 prio=5 os_prio=0 tid=0x00007f84e0012800 nid=0x45e2 waiting on condition [0x0000000000000000]
  • java.lang.Thread.State: RUNNABLE
  • ...