jvm 参数设置

124 阅读27分钟

jvm 内存结构

image.png

由上图可以清楚的看到JVM的内存空间分为3大部分:

  1. 堆内存,堆管存储。会发生OutOfMemoryError异常
  2. 方法区,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。会发生OutOfMemoryError异常
  3. 栈内存,栈管运行。会发生tackOverFlowError和OutOfMemoryError异常

其中栈内存可以再细分为java虚拟机栈和本地方法栈,堆内存可以划分为新生代和老年代,新生代中还可以再次划分为Eden区、From Survivor区和To Survivor区。

其中一部分是线程共享的,包括 Java 堆和方法区;另一部分是线程私有的,包括虚拟机栈和本地方法栈,以及程序计数器这一小部分内存。

堆内存

堆内存是所有线程共有的,可以分为两个部分:年轻代和老年代。

下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代已经被移除

                       

新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 )

默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

方法区

方法区也称"永久代",它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。

在JDK8之前的HotSpot JVM,存放这些”永久的”的区域叫做“永久代(permanent generation)”。永久代是一片连续的堆空间,在JVM启动之前通过在命令行设置参数-XX:MaxPermSize来设定永久代最大可分配的内存空间,默认大小是64M(64位JVM默认是85M)。

随着JDK8的到来,JVM不再有 永久代(PermGen)。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory。

jvm 参数设置

image.png

性能参数

性能参数往往用来定义内存分配的大小和比例,相比于行为参数和调试参数,一个比较明显的区别是性能参数后面往往跟的有数值,常用如下:

指令描述示例备注
-Xms设置堆的最小空间大小-Xmx2g
-Xmx设置堆的最大空间大小-Xmx2g
-Xmn设置年轻代大小-Xmn1g整个堆大小=年轻代大小+年老代大小,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8
-Xss设置每个线程的堆栈大小-Xss256k
-XX:InitialHeapSize设置堆的最小空间大小-XX:InitialHeapSize=2048m
-XX:MaxHeapSize设置堆的最大空间大小-XX:MaxHeapSize=2048m
-XX:ThreadStackSize设置每个线程的堆栈大小-XX:ThreadStackSize=256k
-XX:NewSize设置年轻代最小空间大小-XX:NewSize=2.125m
-XX:MaxNewSize设置年轻代最大空间大小-XX:MaxNewSize=2.125m
-XX:PermSize设置永久代最小空间大小-XX:PermSize=64m
-XX:MaxPermSize设置永久代最大空间大小-XX:MaxPermSize=64m
-XX:ParallelGCThreads配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等-XX:ParallelGCThreads=20
-XX:MaxHeapFreeRatio=70堆内存使用率大于70时扩张堆内存,如果最大堆内存=初始堆内存时该参数无效,默认值70-XX:MaxHeapFreeRatio=70
-XX:MinHeapFreeRatio=40堆内存使用率小于40时缩减堆内存,如果最大堆内存=初始堆内存时该参数无效,默认值40-XX:MinHeapFreeRatio=40
-XX:SurvivorRatio=6S区和Eden区占新生代比率为1:6,两个S区1:1:6-XX:SurvivorRatio=6
-XX:MetaspaceSize初始化的Metaspace大小-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSizeMetaspace最大值-XX:MaxMetaspaceSize=128m
-XX:PretenureSizeThreshold大于该值的对象直接晋升入老年代(这种对象少用为好)-XX:PretenureSizeThreshold=128m
-XX:MaxTenuringThreshold对象在新生代存活区切换的次数(坚持过MinorGC的次数,每坚持过一次,该值就增加1),大于该值会进入老年代(年龄阈值)-XX:PretenureSizeThreshold=15

行为参数

行为参数主要用来选择使用什么样的垃圾收集器组合,以及控制运行过程中的GC策略等

参数及其默认值描述
-XX:UseSerialGC使用Serial 收集器
-XX:+UseParNewGC使用ParNew+Serial Old收集器组合
-XX:+UseParallelGC选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:MaxGCPauseMillis最大垃圾收集停顿时间,减少GC的停顿时间是以牺牲吞吐量和新生代空间来换取的,不是设置的越小越好
-XX:GCTimeRatio设置吞吐量大小,值是大于0小于100的范围,相当于吞吐量的倒数,比如设置成99,吞吐量就为1/(1+99)=1%
-XX:UseAdaptiveSizePolicy是一个开关参数,打开之后,就不需要设置新生代大小(-Xmn)、Eden和Survival的比例(-XX:SurvivalRatio)、 晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数,收集器会自动调节这些参数。
XX:CMSInitiatingOccupancyFraction参数控制触发垃圾回收的阈值
-XX:ParallelGCThreads设置执行内存回收的线程数,在+UseParNewGC的情况下使用
-XX:+UseParallelOldGC使用Parallel Scavenge +Parallel Old组合收集器
-XX:+UseConcMarkSweepGC使用ParNew+CMS+Serial Old组合并发收集,优先使用ParNew+CMS,当用户线程内存不足时,采用备用方案Serial Old收集。
-XX:-DisableExplicitGC禁止调用System.gc();但jvm的gc仍然有效
-XX:+ScavengeBeforeFullGC新生代GC优先于Full GC执行
-XX:+AlwaysPreTouch虽然通过JVM的参数-Xmx和-Xms可以设置JVM的堆大小,但是此时操作系统分配的只是[虚拟内存]而使用【-XX:+AlwaysPreTouch】参数能够达到的效果就是,在服务启动的时候真实的分配物理内存给JVM,而不再是虚拟内存,效果是可以加快代码运行效率,缺点也是有的,毕竟把分配物理内存的事提前放到JVM进程启动时做了,自然就会影响JVM进程的启动时间,导致启动时间降低几个数量级
-XX:MaxDirectMemorySize=128M本地直接内存大小。适合频繁的IO操作
-XX:MetaspaceSize=256m最小元空间大小,1.8及以后适用
XX:MaxMetaspaceSize=256最大元空间大小,1.8及以后适用
-XX:CMSInitiatingOccupancyFraction=72老年代垃圾占比达到这个阈值开始CMS收集,设置过高容易导致并发收集失败,会出现SerialOld收集的情况
-XX:CMSFullGCsBeforeCompaction=1控制FullGC压缩的间隔。多少次不压缩的FullGC之后来一次带压缩的
-XX:NewRatio=4新生代:老年代=1:4
-XX:SurvivorRatio=82个survivor和eden的比值。表示2:8默认为8,也就是说Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10
-XX:+UseG1GC使用G1垃圾回收
-XX:MaxGCPauseMillis=150GC最大暂停时间
-XX:G1NewSizePercent新生代最小值比例。默认5%
-XX:G1MaxNewSizePercent=70新生代最大值比例
-XX:ParallelGCThreads=8STW期间,并行GC线程数
-XX:ConcGCThreads=2并发标记阶段,并行执行的线程数
-XX:-UseBiasedLocking禁止偏向锁
-XX:+ParallelRefProcEnabled启用并行引用处理

调试参数

参数及其默认值描述
-XX:-CITime打印消耗在JIT编译的时间
-XX:ErrorFile=./hs_err_pid.log保存错误日志或者数据到文件中
-XX:-ExtendedDTraceProbes开启solaris特有的dtrace探针
-XX:HeapDumpPath=./java_pid.hprof指定导出堆信息时的路径或文件名
-XX:-HeapDumpOnOutOfMemoryError当首次遭遇OOM时导出此时堆中相关信息
-XX:OnError=";"出现致命ERROR之后运行自定义命令
-XX:OnOutOfMemoryError=";"当首次遭遇OOM时执行自定义命令
-XX:-PrintClassHistogram遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同
-XX:-PrintConcurrentLocks遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同
-XX:-PrintCommandLineFlags打印在命令行中出现过的标记
-XX:-PrintCompilation当一个方法被编译时打印相关信息
-XX:-PrintGC每次GC时打印相关信息
-XX:-PrintGC Details每次GC时打印详细信息
-XX:-PrintGCTimeStamps打印每次GC的时间戳
-XX:-PrintGCTimeStamps打印每次GC的时间戳
-XX:-TraceClassLoading跟踪类的加载信息
-XX:-TraceClassLoadingPreorder跟踪被引用到的所有类的加载信息
-XX:-TraceClassResolution跟踪常量池
-XX:-TraceClassUnloading跟踪类的卸载信息
-XX:-TraceLoaderConstraints跟踪类加载器约束的相关信息
-XX:+PrintGCApplicationStoppedTime打印垃圾回收期间程序暂停的时间
-XX:AutoBoxCacheMax=10000如果项目中需要经常对更大的整数类型进行转换的话,那么就可以将这个参数修改得更大些

典型JVM参数配置参考:

  • java-Xmx3550m-Xms3550m-Xmn2g-Xss128k
  • -XX:ParallelGCThreads=20
  • -XX:+UseConcMarkSweepGC-XX:+UseParNewGC

-Xmx3550m:设置JVM最大可用内存为3550M。

-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大 小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000 左右。

垃圾回收算法

标记-清除

先标记出所有要回收的对象,在标记完成后统一进行对象的回收。有两个不足:

  • 1 是效率问题,标记和清除的效率都不高。

  • 2 是空间问题,会产生大量不连续的内存碎片,碎片太多会都导致大对象无法找到足够的内存,从提前触发垃圾回收。

    CMS回收器

复制算法

新生代分为一个Eden,两个Survival空间,默认比例是8:1。回收时,将Eden和一个Survival的存活对象全部放入到另一个Survival空间中,最后清理掉刚刚的Eden和Survival空间 。当Survival空间不够时,由老年代进行内存分配担保

Serial 回收器

ParallelNew 回收器

Parallel Scavenge收集器

Serial Old 收集器

Palallel Old 收集器

标记- 整理

根据老年代对象的特点,先标记存活对象,将存活对象移动到一端,然后直接清理掉端边界以外的对象

G1回收器

分代收集

新生代采用复制算法,老年代采用标记-删除,或者标记-整理算法。

jvm 垃圾回收器

如果两个收集器之间有连线,说明可以搭配使用。没有最好的收集器,也没有万能的收集器,只有对应具体应用最合适的收集器。 image.png

Serial收集器

  • 特点:新生代单线程回收。优点是简单高效,对于运行在client模式的虚拟机是很好的选择

  • 工作分代:新生代

  • 配置:

-XX:UseSerialGC 使用Serial收集器

ParNew收集器

  • 特点: Serial的多线程版本,除了Serial收集器之外,只有它能与CMS收集器配合工作。ParNew收集器在单CPU的环境中,效果不如Serial好,随着CPU的增加,对于GC时系统资源的利用还是很有效的。 默认开启的收集线程数和CPU数相等,可以使用 -XX:ParallelGCThreads 指定

  • 工作分代:新生代

  • 配置:

-XX:+UseConcMarkSweepGC CMS收集器

-XX:+UseParNewGC 强制使用ParNew收集器

-XX:ParallelGCThreads=size(默认cpu数量)设置线程数量

Parallel Scavenge收集器

  • 特点: 新生代收集器,并行收集器,复制算法,和其他收集器不同,关注点的是吞吐量(垃圾回收时间占总时间的比例)。提供了两个参数用于控制吞吐量。

  • 工作分代:新生代

  • 算法:复制

  • 配置:

-XX:MaxGCPauseMillis 最大垃圾收集停顿时间,减少GC的停顿时间是以牺牲吞吐量和新生代空间来换取的,不是设置的越小越好

-XX:GCTimeRatio 设置吞吐量大小,值是大于0小于100的范围,相当于吞吐量的倒数,比如设置成99,吞吐量就为1/(1+99)=1%。

-XX:UseAdaptiveSizePolicy 这是一个开关参数,打开之后,就不需要设置新生代大小(-Xmn)、Eden和Survival的比例(-XX:SurvivalRatio)、 晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数,收集器会自动调节这些参数。

Serial Old 收集器

  • 特点:单线程收集器,老年代,主要意义是在Client模式下的虚拟机使用。在Server端,用于在JDK1.5以及之前版本和Parallel Scavenge配合使用,或者作为CMS的后备预案。

  • 工作分代:老年代

  • 算法:复制

  • 配置

Palallel Old 收集器

  • 特点: 是Parallel Scavenge的老年代版本。在注重吞吐量的场合,都可以优先考虑Parallel Scavenge 和Palallel Old 配合使用

  • 工作分代:老年代

  • 算法:复制

  • 配置

CMS收集器

  • 特点: Concurrent Mark Sweep,是一种以获取最短回收停顿时间为目标的收集器,尤其重视服务的响应速度。基于标记-清除算法实现。分为四个步骤进行垃圾回收:初始标记,并发标记,重新标记,并发清除。只有初始标记和重新标记需要停顿。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记就是进行GC Roots的Tracing。重新标记为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间会比初始标记阶段稍长,远比并发时间短。耗时最长的并发标记和并发清除过程中,处理器可以与用户线程一起工作。它并不是完美的,有如下三个比较明显的缺点:
  1. 垃圾回收时会占用一部分线程,导致系统变慢,总吞吐量会降低。

  2. 无法处理浮动垃圾,需要预留足够的内存空间给用户线程使用,可以通过 -XX:CMSInitiatingOccupancyFraction 参数控制触发垃圾回收的阈值。如果预留的内存无法满足程序需要,就会出现“Concurrent Mode Failure”失败,这时将启动应急预案,启用Serial Old 进行垃圾回收,停顿时间会变长。所以-XX:CMSInitiatingOccupancyFraction 参数的值设置的太高,会导致频繁“Concurrent Mode Failure”失败,性能反而降低。

  3. 标记-清理,容易产生内存碎片。-XX:+UseCMSCompactAtFullColletion 开启碎片整理功能,默认开启,-XX:CMSFullGCsBeforeCompaction,控制多少次不压缩的FullGC之后来一次带压缩的

  • 工作分代:老年代

  • 算法:标记-清理

  • 配置 -XX:+UseConcMarkSweepGC cms 收集器

G1 收集器

  • 特点:

    包括新生代和老年代的垃圾回收。和其他收集器相比的优点:并行和并发,分代收集,标记-整理,可预测的停顿。垃圾回收分为以下几个步骤:

  1. 初始标记:标记GC Roots能够直接关联到的对象,这阶段需要停顿线程,时间很短
  2. 并发标记:进行可达性分析,这阶段耗时较长,可与用户程序并发执行
  3. 最终标记:修正发生变化的记录,需要停顿线程,但是可并行执行
  4. 筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来执行回收计划
  • 工作分代:新生代 老年代

  • 算法:标记-整理

  • 配置

    -XX:+UseG1GC

GC的分类

分类年前代老年代方法区
部分收集新生代收集(Minor GC/YGC)
部分收集老年代收集(Major GC/Old GC):只收集老年代 目前只有CMS GC会有单独收集老年代的行为
部分收集混合收集(Mixed GC):收集整个新生代以及部分老年代 G1 GC会有这种行为混合收集(Mixed GC):收集整个新生代以及部分老年代 G1 GC会有这种行为
整堆收集Full GCFull GCFull GC

常见的JVM配置

CPU 核数:8;内存(GB):16;磁盘(GB):400 ; G1

-Xms10g 最小堆大小
   
-Xmx10g 最大堆大小

-Xss512k 栈容量

-XX:MetaspaceSize=256m 最小元空间

-XX:MaxMetaspaceSize=256m 最大元空间

-XX:MaxDirectMemorySize=128M   本地直接内存,默认和堆内存最大值一样(-Xmx)
    
-XX:+UseG1GC 使用G1垃圾回收

-XX:MaxGCPauseMillis=150 GC最大暂停时间

-XX:+ParallelRefProcEnabled 启用并行引用处理

-XX:+UnlockExperimentalVMOptions 有些时候当设置一个特定的JVM参数时,JVM会在输出“Unrecognized VM option”后终止。如果参数输入是正确的,并且JVM并不识别,需要设置-XX:+UnlockExperimentalVMOptions 来解锁参数。

-XX:G1MaxNewSizePercent=70 年轻代最小值比例。默认5%

-XX:ParallelGCThreads=8 STW期间,并行GC线程数
 
-XX:ConcGCThreads=2  并发标记阶段,并行执行的线程数
 

CPU 核数:4;内存(GB):8;磁盘(GB):200;CMS+ParNew

-Xmx4g   最小堆大小
    
-Xms4g   最大堆大小
    
-XX:MetaspaceSize=256m   最小元空间
    
-XX:MaxMetaspaceSize=256m   最小元空间
    
-XX:SurvivorRatio=8  2个survivor和eden的比值。表示28
默认为8,也就是说Eden占新生代的8/10From幸存区和To幸存区各占新生代的1/10
    
-XX:NewRatio=4  新生代:老年代=14
    
-XX:+HeapDumpOnOutOfMemoryError  内存溢出异常时Dump出当前的内存堆转储快照以便日后分析
    
-XX:+DisableExplicitGC  关闭System.gc()
    
-XX:+PrintGCDetails   打印GC详细日志
    
-XX:+UseConcMarkSweepGC  指定CMS收集器
    
-XX:ParallelGCThreads=4  指定回收线程数量
    
-XX:+CMSClassUnloadingEnabled  CMS收集器默认不会对永久代进行垃圾回收。如果希望对永久代进行垃圾回收,可以设置标志
    
-XX:CMSFullGCsBeforeCompaction=1  控制FullGC压缩的间隔。多少次不压缩的FullGC之后来一次带压缩的
    
-XX:CMSInitiatingOccupancyFraction=72 参数控制触发垃圾回收的阈值。如果预留的内存无法满足程序需要,就会出现“Concurrent Mode Failure”失败,这时将启动应急预案,启用Serial Old 进行垃圾回收,停顿时间会变长。所以-XX:CMSInitiatingOccupancyFraction 参数的值设置的太高,会导致频繁“Concurrent Mode Failure”失败,性能反而降低。
    
    
    

CPU 核数:8;内存(GB):32;磁盘(GB):200;CMS+ParNew

if [ "${ENVIRONMENT}" = "prod" ]; then
    HEAP_OPTS="-Xmx8g -Xms6g -Xmn3g -Xss256k"
    # -Xmx8g 堆最大8G
    # -Xms6g 堆最小8G
    # -Xmn3g 年前代3G,官方建议3/8
else
    HEAP_OPTS="-Xmx1g -Xms1g"
    DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8989"
fi

PERM_OPTS="-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+AlwaysPreTouch -XX:-UseBiasedLocking -XX:AutoBoxCacheMax=10000 -XX:+ParallelRefProcEnabled -Djava.security.egd=file:/dev/./urandom -Djava.net.preferIPv4Stack=true"
    
    # XX:MetaspaceSize=128m 元空间初始值128M
    # -XX:MaxMetaspaceSize=512m 元空间最大512M
    # -XX:+AlwaysPreTouch 设置堆大小其实分配的是虚拟内存,当前配置表示分配物理内存
    # XX:-UseBiasedLocking 开启偏向锁
    # -XX:AutoBoxCacheMax=10000 如果项目中需要经常对更大的整数类型进行转换的话,那么就可以将这个参数修改得更大些
    # -XX:+ParallelRefProcEnabled 启用并行引用处理
GC_OPTS="-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=6 -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSWaitDuration=10000 -XX:+ExplicitGCInvokesConcurrent -Xloggc:${GC_LOG_DIR}/gc.log.${nowday} -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails"

虚拟机性能监控与故障处理工具

jps

命令语法:jps [options] [hostid]

options:命令选项,用来对输出格式进行控制

hostid:指定特定主机,可以是ip地址和域名, 也可以指定具体协议,端口。

[protocol:][[//]hostname][:port][/servername]

功能描述:jps是用于查看有权访问的hotspot虚拟机的进程. 当未指定hostid时,默认查看本机jvm进程

常见参数:-lmvV

命令使用

  • jsp 列出进程id和className

image.png

  • jps -q 只列出进程id

image.png

  • jps -m 列出进程id,主函数名称,执行程序从命令行输入的参数

image.png

  • jps -l 列出进程id,包名.函数名

image.png

  • jsp -v 列出进程id,jvm 参数

image.png

  • jsp -V 输出通过.hotsportrc或-XX:Flags=指定的jvm参数

jstat

命令语法:jstat - [-t] [-h] [ []]

Options — 选项,我们一般使用 -gcutil 查看gc情况

vmid    — VM的进程号,即当前运行的java进程号

interval– 间隔时间,单位为秒或者毫秒

count   — 打印次数,如果缺省则打印无数次

功能描述:Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。可见,Jstat是轻量级的、专门针对JVM的工具,非常适用。由于JVM内存设置较大,图中百分比变化不太明显

命令使用示例

S0  — Heap上的 Survivor space 0 区已使用空间的百分比
S1  — Heap上的 Survivor space 1 区已使用空间的百分比
E   — Heap上的 Eden space 区已使用空间的百分比
O   — Heap上的 Old space 区已使用空间的百分比
P   — Perm space 区已使用空间的百分比
M   — 元空间使用百分比
CCS   — 压缩类容量使用百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)

  • jstat -gcutil pid 列出gc状态

image.png

  • jstat -gcutil pid interval count 动态列出GC状态
    如果young gc和full gc能够正常发生,而且都能有效回收内存,常驻内存区变化不明显,则说明java内存释放情况正常,垃圾回收及时,java内存泄露的几率就会大大降低。但也不能说明一定没有内存泄露。

image.png

  • jstat -class pid 显示有关类加载器行为的统计信息

image.png

字段说明
Loaded 类加载器加载的类的数量
Bytes 类加载器加载的类的字节数
Unloaded 类加载器未加载的类的数量
Bytes 类加载器未加载的类的字节数
Time 执行类加载和卸载操作所花费的时间

  • jstat -compiler pid 显示有关Java HotSpot VM即时编译器行为的统计信息

image.png

  • jstat -gc pid 显示有关垃圾收集堆行为的统计信息
    image.png 字段名 | 说明 | | ---- | --------------------- | | S0C | 第一个Survivor的容量(kB) | | S1C | 第二个Survivor的容量(kB) | | S0U | 第一个Survivor已使用的容量(kB) | | S1U | 第二个Survivor已使用的容量(kB) | | EC | Eden区的容量(kB) | | EU | Eden区已使用的容量(kB) | | OC | 老年代的容量(kB) | | OU | 老年代已使用的容量(kB) | | MC | 元空间的容量(kB) | | MU | 元空间已使用的容量(kB) | | CCSC | 压缩类的容量(kB) | | CCSU | 压缩类已使用的的容量(kB) | | YGC | 年轻代垃圾收集的次数 | | YGCT | 年轻代垃圾回收消耗时间 | | FGC | 老年代垃圾回收次数 | | FGCT | 老年代垃圾回收消耗时间 | | GCT | 垃圾回收消耗总时间

  • jstat -gccapacity pid

image.png

字段名说明(单位KB)
NGCMN新生代最小大小
NGCMX新生代最大大小
NGC当前新生代的大小
S0C第一个Survivor的大小
S1C第二个Survivor的大小
ECEden区的大小
OGCMN老年代最小大小
OGCMX老年代最大大小
OGC当前老年代的大小
OC当前老年代的大小
MCMN元空间的最小大小
MCMX元空间的最大大小
MC当前元空间的大小
CCSMN最小压缩类空间大小
CCSMX最大压缩类空间大小
CCSC当前压缩类空间大小
YGC年轻代GC次数
FGCFull GC的次数
  • jstat -gccause pid 显示关于垃圾收集统计信息的摘要(与-gcutil相同),以及最近和当前(适用时)垃圾收集事件的原因

image.png

字段名称说明
S0第一个Survivor区的利用率。第一个Survivor区的当前容量 / 第一个Survivor区的总容量 * 100
S1第二个Survivor区的利用率。第二个Survivor区的当前容量 / 第二个Survivor区的总容量 * 100
EEden的利用率。Eden区的当前容量 / Eden区的总容量 * 100
O老年代的利用率。老年代的当前容量 / 老年代的总容量 * 100
M元空间的利用率。元空间的当前容量 / 元空间的总容量 * 100
CCS压缩类空间的利用率。压缩类空间的当前容量 / 压缩类空间的总容量 * 100
YGC年轻代发生GC的次数
YGCT年轻代发生GC的耗时
FGC FullGC的次数
FGCTFull GC的耗时
GCTGC的总耗时
LGCC上次垃圾回收的原因
GCC当前垃圾回收的原因
  • jstat -gcnew pid 显示新生代的行为统计信息

image.png

字段名说明
S0C第一个Survivor区的大小(KB)
S1C第二个Survivor区的大小(KB)
S0U第一个Survivor区已经使用掉的大小(KB)
S1U第二个Survivor区已经使用掉的大小(KB)
TT晋升到老年代的阈值。每经历一次GC,对象如果没有被回收,则它的年龄就会+1,如果对象的年龄大于设置的一个晋升的阈值,该对象就会晋升到老年代中
MTT晋升到老年代的阈值的最大值。对象最多经过MTT次GC而没有被回收的话,就会晋升到老年代中
DSS所需的Survivor区的大小(KB)
ECEden区的大小(KB)
EUEden区已使用的大小(KB)
YGC年轻代GC的次数
YGCT年轻代GC的耗时
  • jstat -gcnewcapacity pid 显示新生代及其对应的空间大小的统计信息

image.png

字段名说明
NGCMN新生代的最小容量(KB)
NGCMX新生代的最大容量(KB)
NGC新生代当前的容量(KB)
S0CMX第一个Survivor区的最大容量(KB)
S0C第一个Survivor区当前的容量(KB)
S1CMX第二个Survivor区的最大容量(KB)
S1C第二个Survivor区当前的容量(KB)
ECMXEden区的最大容量(KB)
ECEden区当前的容量(KB)
YGC新生代GC的次数
FGCFull GC的次数
  • jstat -gcold pid 显示老年代和元空间的统计信息

image.png

字段名说明
MC元空间的大小(KB)
MU元空间已使用的大小(KB)
CCSC压缩类空间的大小(KB)
CCSU压缩类空间已使用的大小(KB)
OC老年代的大小(KB)
OU老年代已使用的大小(KB)
YGC年轻代GC的次数
FGCFULL GC的次数
FGCTFULL GC花费的时间
GCTGC花费的总时间
  • jstat -gcoldcapacity pid

image.png

字段名说明
OGCMN老年代最小大小(KB)
OGCMX老年代最大大小(KB)
OGC老年代当前大小(KB)
OC老年代当前大小(KB)
YGC年轻代GC的次数
FGCFULL GC的次数
FGCTFULL GC的耗时
GCTGC总耗时
  • jstat -gcmetacapacity pid 显示元空间的大小统计信息

image.png

字段名说明
MCMN元空间的最小大小(KB)
MCMX元空间的最大大小(KB)
MC元空间当前的大小(KB)
CCSMN压缩类空间的最小大小(KB)
CCSMX压缩类空间的最大大小(KB)
CCSC压缩类空间当前的大小(KB)
YGC年轻代GC的次数
FGCFULL GC的
FGCT
GCT
  • jstat -printcompilation pid 显示Java HotSpot虚拟机编译方法统计信息

image.png

字段名说明
Compiled由最近编译的方法执行的编译任务数
Size最近编译的方法的字节码的字节数
Type最近编译的方法的编译类型
Method标识最近编译的方法的类名和方法名。类名使用斜杠(/)而不是点(.)作为名称分隔符,方法名是指定类中的方法。 格式与-XX:+PrintCompilation参数的格式一致的。
  • jstat -printcompilation -h 2 pid

    每2行打印一次标题头 image.png

  • jstat -printcompilation -t pid 显示每行的时间戳

image.png

jmap

命令语法:jmap [options] [pid]

options:命令选项
-heap: 输出jvm 堆详细信息
-histo[:live]: 显示堆中对象的统计信息,[:live] 显示存活的对象。JVM会先触发gc,然后再统计信息,慎用
-finalizerinfo: 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象(显示即将回收的对象)
-dump:live,format=b,file=heap 生成堆转储快照。慎用,容易挂起java线程或者宕机
-F 当-dump没有响应时,强制生成dump快照
-H 帮助

功能描述:JVM Memory Map命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候自动生成dump文件。 jmap不仅能生成dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等

常见参数:[-heap][-histo][-permstat][-finalizerinfo][-dump][-F -dump]

命令使用示例

  • jmap -heap

输出jvm 堆详细信息
image.png

image.png

  • jmap -F -histo 21314 或者 jmap -histo[:live] 21314

显示堆中对象的统计信息,[:live] 显示存活的对象。JVM会先触发gc,然后再统计信息,慎用

image.png

image.png

  • jmap -finalizerinfo

显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象(显示即将回收的对象)

image.png

  • dump:live,format=b,file=heap

生成堆转储快照。慎用,容易挂起java线程或者宕机。format指定输出格式,live指明是活着的对象,file指定文件名

jstack

命令语法:jstack [-l] pid

功能描述
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的

命令使用示例

  • jstack -F PID

没有相应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack,一般情况不需要使用

  • jstack -l PID

长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间),-l 建议不要用。一般情况不需要使用

  • jstack -m PID

打印java和native c/c++框架的所有栈信息.可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用

  • jstack -h PID

打印帮助信息

jvm 常见问题案例分析

排查出cpu 消耗过高的代码

  1. 模拟消耗cpu的代码片段
   
@RequestMapping("/cpuLoad")
public ResponseDto cpuLoad() {
    Runnable target;
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            long i = 0;
            while (true) {
                i++;
            }
        }
    });
    thread.setName("rumenz");
    thread.start();
    return new ResponseDto().data(null);
}
    
  1. 检查cpu 消耗过高的进程,找出进程号(pid)
    linux 使用top 命令查看

image.png

win10 使用资源管理器查看。推荐资源管理连接download.sysinternals.com/files/Proce…

  1. 检查已知进程下cpu 消耗高的线程id

linux 使用命令:top -Hp [步骤2得出的进程号(pid)]

window 使用步骤2 推荐的资源管理器查看 线程id

得出线程id后转成16进制,步骤4需要: printf "%x\n" 2238

  1. 检查已知进程下cpu 消耗高的线程id

打印堆栈信息: jstack [pid] |grep -A 50 [步骤3线程id的16进制] 如下图

image.png

  1. 示例

image.png

image.png

image.png

image.png