JVM调优详解

JVM调优详解

说到jvm优化,大多数人认为只需要通过调整jvm参数来提升jvm性能。经过阅读《深入理解java虚拟机》、查看相关博客文章及一些相关视频,在深入了解jvm底层与实操过系统jvm调优后,个人认为虚拟的调优可以从如下几个方面进行。

1、选择合适收集器

众所周知,软件系统所承载的业务千差万别。有些需要与用户频繁交互(IO密集型),有些需要大量的运算(CPU密集型)。根据业务的不同,我们需要选择适合业务的垃圾收集器,以最大限度的提升系统性能。如果搭配不当很有可能会影响到系统性能,导致虚拟机频繁GC、而且可能出现内存溢出异常。

举个真实存在的例子。

公司有一套租务系统,该系统主要承载的是租务相关的业务,属于IO密集型系统。运维人员启动系统的时设置了【 -Xmx1G -Xms1G -XX:+UseG1GC】参数,即最小堆内存为1G、最大堆内存为1G、并使用了G1收集器,其它参数使用默认的。该系统运行在一段时候后,出现用户请求响应缓慢,甚至一度出现了内存溢出异常。运维将问题转交给开发人员。开发人员在生产环境中,植入阿里的Arthas工具,通过【dashboard】查看到GC频繁异常。并定位到out of memory 异常的代码。

从jvm的启动参数中,我们不难看出该系统目前使用的G1收集器,G1收集器虽然说相对比较先进的收集器,但并非适用于所有的场景。它的内存结构中使用了记忆集相关技术,记忆集将会占用堆的10%-20%内存,且当堆内存占用达到45%(默认值,可以通过参数调整)时就会进行混合回收,也就是说1G的内存实际能使用的也就只有800M-900M、当对象创建频繁很快就会达到阈值,执行混合回收,导致GC频繁,GC过程中无法避免的会出现STW,影响到用户线程。所以说在只有1G内存的情况下,使用G1收集器是不合适。后来更换为pernew+CMS收集器后,系统GC频率和用户响应速度变正常了。

所以说选择合适的收集器也是至关重要的。

下面介绍一下jdk8/jdk9前的收集器组合。
在这里插入图片描述

收集器组合新生代算法老年代算法特点适用场景启用参数
Serial + Serial Old复制算法标记-压缩算法响应优先单CPU环境client模式、小内存。-XX:+UseSerialGC
ParNew + CMS复制算法标记-清除算法响应优先多CPU环境server模式、较大内存。适用于互联网或B/S业务。-XX:+UseConcMarkSweepGC
Parallel + Parallel Old复制算法标记-压缩算法吞吐量优先多CPU环境server模式、较大内存。适用于运算多交互少的场景。-XX:+UseParallelGC
G1复制算法标记-压缩算法保证响应优先且提高吞吐量多CPU环境server模式、大内存。服务端应用、适用于互联网或B/S业务。-XX:+UseG1GC

选择规则

在这里插入图片描述

1.1 Serial回收器

Serial收集器是历史最悠久的垃圾收集器、是一款串行单线程收集器,也是hotspot的client模式下的新生代垃圾收集器。默认情况下与Serial Old搭配使用。

Serial收集器为年轻代收集器采用复制算法不会产生内存碎片,Serial Old 收集器为老年代收集器采用标记-压缩算法,也不会产生内存碎片。

在这里插入图片描述

Serial收集器无论在新生代还是老年代收集垃圾时,都会产生STW,暂停所有用户线程,直至垃圾收集清除完毕。

  • 优势

    对于单CPU环境而言,Serial收集器没有切换线程上下文的开销,性能上还是相对可观的。在client模式下选择该收集器是一个不错的选择。

  • 劣势

​ 随着多核CPU的出现,使用到该收集器的场景越来越少。

1.2 ParNew回收器

ParNew回收器是多线程并行收集器,与Serial回收器的区别是,一个是单线程版本、一个是多线程版本。默认情况下与CMS搭配使用。

ParNew收集器为年轻代收集器采用复制算法不会产生内存碎片,CMS收集器为老年代收集器,该收集器主打一个低延迟,采用的是标记-清除算法,会产生

一定量的内存碎片,当老年代内存无法满足对象分配要求时,将会采用备用方案【Serial Old 收集器】用于收集垃圾,让内存变的规整。

在这里插入图片描述

  • 优势

​ ParNew 收集器作用在多CPU环境下,充分利用多CPU、多核心资源优势。

  • 劣势

    在单CPU的环境下,其性能不如Serial收集器,需要额外付出多线程间上下文切换等开销

1.3 Parallel Scavenge 回收器

Parallel回收器是多线程并行收集器,与ParNew不同的是,该收集器主打的是可控制的吞吐量,即吞吐量优先的垃圾收集器,其具备自适应调节策略,可动态调整堆内存大小来达到提高吞吐量的目的。比如在新生代中,伊甸园区和幸存者区的默认比例是8:1:1 但是当你配置了收集器为Parallel 时,会发现伊甸园区和幸存者区的比例为6:1:1 ,这是由于自适应调节策略导致的。默认情况下与Parallel Old 搭配使用。是jdk8默认的收集器,即如果使用的是jdk8,没有特定指定收集器,系统将会使用该收集器。

Parallel Scavenge 收集器为年轻代收集器采用复制算法不会内存碎片,Parallel Old 也是多线程并行收集器,为老年代收集器采用标记-压缩算法也不会产生内存碎片。

因为该收集器是以吞吐量优先为特点,可以配置吞吐量比例【-XX:GCTimeRatio=(1/(N + 1))】N为(0,100)之间的值,而自适应调节策略参数为【-XX:+UseAdaptiveSizePolicy】,该参数可以不用显式配置,默认就存在。

在这里插入图片描述

  • 优势

​ Parallel 收集器作用在多CPU环境下,充分利用多CPU、多核心资源优势,同时适用于那么需要运算能力相对比较高的系统。

  • 劣势

​ 在单CPU的环境下,其性能不如Serial收集器,需要额外付出多线程间上下文切换等开销

1.4 CMS 回收器

CMS回收器为多线程并发收集器,主打低延迟,是Hot spot 虚拟机第一次实现了让垃圾收集线程和用户线程同时工作。该收集器尽可能缩短垃圾收集时用户线程的停顿时间,提升响应速度。从而给用户带来更好的交互体验。

CMS收集器为老年代收集器采用标记-清除算法,在收集清除垃圾过程中会产生内存碎片,但是老年代碎片过多,无法提供足够的内存分配给对象时 ,将会采用备用方案,启用serial-old进行垃圾收集,并将内存规整。一旦采用备用方案,STW将会延长。

在这里插入图片描述

CMS之所以能达到低延迟,在于其耗时比较长的步骤基本上都是与用户线程同时进行,如并发标记和并发清除,初始标记和重新标记耗时都比较小,所以相对与其它收集器而言,其在响应效率上有了显著的提升。

  • 优势

    CMS收集器作用在多CPU环境下,充分利用多CPU、多核心资源优势。适用于B/S业务与电商业务。

  • 劣势

    产生内存碎片,无法处理浮动垃圾。在并发标记阶段,因为用户线程也在执行、如果在阶段产生了新的垃圾对象,我们称之为浮动垃圾,收集器将无法对这部分垃圾机型收集。

1.5 G1 回收器

G1是一款并行收集器主打的是低延迟,那么既然有了CMS这种追求低延迟的收集器,为什么还要设计G1收集器呢。时代在进步,业务在增长。现有的GC收集器已经无法完全保证系统的正常运行。且造成的STW也跟不上需求。

G1的目标是在保证延迟可控的情况下追求最大程度的吞吐量,该收集器既适用于年轻代也适用于与老年代,新老通吃。年轻代采用复制算法,老年代使用标记整理算法。

G1将堆内存划分为多个区域。使用不同的区域表示伊甸园、幸存者0区、幸存者1区、老年代、巨型对象等。

G1收集器避免全区域垃圾收集。其会跟踪每个区域的价值。优先回收价值比较的大的区域。如某一个区域存放都是垃圾对象,收集器会认为该区域的回收价值很高,待到执行GC时,将会优先收集。

G1具备并发与并行特点:在垃圾收集期间有多个GC线程同时进行并具备与应用程序交替执行的能力。

G1也分代收集,其将堆内存划分为新生代和老年代。当然因为区域化的原因,新生代和老年代的划分只是逻辑上的,如,标记为伊甸园、幸存者0区与幸存1区的区域为逻辑上新生代,标记为老年代的区域为老年代。

在这里插入图片描述

G1收集器的回收过程只要包括三个环境:年轻代GC、年轻代GC+并发标记、混合回收

在这里插入图片描述
记忆集:

收集器先要确认哪些对象是垃圾对象,将会采用可达性分析排查出尚在使用中的对象。对于其他收集器而言,因为内存相对比较小,所以这个步骤耗费的时间比较短,但是对于G1收集器而言,因为其运行在大内存环境上,如果每次GC的时候都要重新从GCROOT开始遍历分析,将会耗费比较长的时间。为了解决这个问题,G1收集器采用了记忆集的技术,为每个区域分配一块内存用于记录该区域对象的引用关系。在一定程度上提升了标记垃圾对象的速率,但是也额外付出了一定内存空间。

年轻代GC:

年轻代GC采用复制算法,当伊甸园区的内存不足是将会进行GC,在标记垃圾对象时,因为G1采用了记忆集的手段,所以在复制对象到幸存者区的时候,需要先更新记忆集和同步记忆集。在处理完记忆集后才进行存活对象的复制。

并发标记:

并发标记环节类似于CMS的回收过程也会伴随着年轻代GC,也有初始标记、并发标记、和再次标记,唯一的不同在于,在并发标记的时候需要计算区域的价值并记录起来。以便在混合回收的时候,为了达到指定的低延迟标准,清除价值比较高的区域。

混合回收:

混合回收是指年轻代和老年代的回收。年轻代将会采用复制算法清除所有的垃圾对象。而老年代只会清除部分价值比较高的区域。

  • 优势

​ G1收集器作用在多CPU环境下,充分利用多CPU、多核心资源优势。适用于B/S业务与电商业务,特别是针对那些有大内存的服务,G1收集器能够充分利用上。

  • 劣势

    如果内存比较小的情况下,G1收集器时体现不了优势,还不如直接使用CMS收集器

2、配置默认参数

JVM调优往往都是在系统运行了一段时间会后,根据监控的数据,适当的调整参数,提升jvm性能。但是如果刚开始的时候没有监控数据,如何进行参数配置呢。下面将介绍一些常规的参数配置,参考网上的一些文章。

参数配置规则:

1、堆起始内存与堆最大内存基本保持一致,为了避免动态伸缩而浪费资源

2、堆内存设置为总JVM内存的2/3

3、元空间起始内存和最大内存保持一致,可以避免动态伸缩而浪费内存,如果为了避免元空间内存溢出,可以不设置最大元空间内存。

4、设置合适的垃圾收集器,多核CPU、内存大于6G可以尝试选择G1收集器、多核CPU、内存小于6G,追求高吞吐量的可以选择Parallel + Parallel Old

收集器、追求低延迟的可以选择ParNew+CMS 收集器,如果只有单核CPU,选择Serial + Serial Old 收集器。

5、设置打印GC详情,且将GC信息输出到GC日志中,以便使用GC工具分析GC日志。

6、设置将内存溢出异常输出到快照日志中,避免如果在生产环境上真的出现了内存溢出,没有快照就无法分析当时的情况。

内存内存设置GC【低延迟】GC【高吞吐】日志输出
1.5G-Xmx1008M -Xms1008M -XX:MaxMetaspaceSize=128M -XX:MetaspaceSize=128M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
2G-Xmx1344M -Xms1344M -XX:MaxMetaspaceSize=192M -XX:MetaspaceSize=192M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
3G-Xmx2048M -Xms2048M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
4G-Xmx2688M -Xms2688M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
5G-Xmx3392M -Xms3392M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
6G-Xmx4096M -Xms4096M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M-XX:+UseConcMarkSweepGC-XX:+UseParallelGC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
7G-Xmx4736M -Xms4736M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M-XX:+UseG1GC-XX:+UseG1GC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc
8G-Xmx5440M -Xms5440M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M-XX:+UseG1GC-XX:+UseG1GC-XX:+PrintGCTimeStamps -Xloggc:/var/app/gc/gc%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc

3、监控与调优

系统在运行一段时间后,仍然有一定的调优空间,这时我们需要参考实际的数据重新设置JVM参数,调整业务代码。以提高系统的性能和保证系统的稳定性。

其实调优的工具也挺多的,像 JvisualVm、JConsole、MAT等,但是这类工具不太适用于生产环境,只适用于开发环境、测试环境或预生产环境。一旦生产环境出现问题只能通过分析快照来定位问题。

下面介绍的是适用于生产环境的工具,JDK自带的命令行工具和阿里的Arthas.

3.1 命令行

命令行工具一般存放在jdk bin目录下,目前大部分系统部署都是采用容器化部署,如果采用的jre为基础镜像是没有这些工具的,建议使用jdk为基础镜像。

  • jps

    查看系统的java进程

    -q : 仅显示本地虚拟机唯一ID

    在这里插入图片描述

    -l : 输出应用程序主类全类名,如果是jar包,则输出jar完整路径

    在这里插入图片描述

    -m : 输出虚拟机进程传递给主类main()的参数

    在这里插入图片描述

    -v : 列出虚拟机进程启动时的JVM参数

    在这里插入图片描述

  • jstat

    查看JVM统计信息,用于监控虚拟机各种运行状态信息,显示虚拟机进程中类的装载、内存、垃圾收集和JIT编译等信息。常用于检测垃圾回收问题和内存泄漏问题。

    指令格式:

  jstat  -<option> [-t] [-h<lines>] <vmid> [<interval>] [<count>]

-class : 显示与ClassLoader的相关信息,如类的装载、卸载数量、总空间及类装载过程中所消耗的时间等

jps -l 【找到java进程的pid】
jstat -class 19768 【19768为pid】
jstat -class 19768 1000 【19768为pid,1000 为间隔时间,单位为毫秒】
jstat -class 19768 1000 10 【19768为pid,1000 为间隔时间,单位为毫秒,10 为打印次数】
jstat -class -t 19768 【显示启动到现在所花费的时间以及类装载大小】

在这里插入图片描述

-gc : 显示与GC相关的堆信息,包括Eden区、Survive区、老年代永久代等的容量、已用空间、GC时间等信息

jstat -gc 19768
jstat -gc 19768 1000 10【每个1000毫秒 打印10次GC】

在这里插入图片描述

在这里插入图片描述

参数解析:

参数解析
S0C第一个幸存者区空间大小
S1C第二个幸存者区空间大小
S0U第一个幸存者区已使用空间大小
S1U第二个幸存者区已使用空间大小
EC伊甸园区空间大小
EU伊甸园区已使用空间大小
OC老年代空间大小
OU老年代已使用空间大小
MC方法区空间大小
MU方法区已使用空间大小
CCSC压缩类空间大小
CCSU压缩类已使用空间大小
YGC年轻代垃圾回收次数
YGCT年轻代垃圾回收消耗时间
FGC老年代垃圾回收次数
FGCT老年代垃圾回收消耗时间
GCT垃圾回收总耗时

-gccapacity : 雷同 -gc ,以最大空间和最小空间的形式输出java堆的各个区域

jstat -gccapacity 19768
jstat -gccapacity 19768 1000 10

在这里插入图片描述

参数解析

参数解析
NGCMN新生代最小容量
NGCMX新生代最大容量
NGC当前新生代容量
S0C第一个幸存者区空间大小
S1C第二个幸存者区空间大小
S0U第一个幸存者区已使用空间大小
S1U第二个幸存者区已使用空间大小
EC伊甸园区空间大小
OGCMN老年代最小容量
OGCMX老年代最大容量
OGC当前老年代大小
OC老年代空间大小
MCMN元空间最小容量
MCMX元空间最大容量
MC方法区空间大小
CCSMN压缩类空间最小容量
CCSMX压缩类空间最大容量
CCSC压缩类空间大小
CCSU压缩类已使用空间大小
YGC年轻代垃圾回收次数
FGC老年代垃圾回收次数

-gcutil : 雷同-gc,以百分比的形式输出java堆的各个区域

jstat -gcutil 19768
jstat -gcutil 19768 1000 10

在这里插入图片描述

参数解析
S0第一个幸存者区当前使用比例
S1第二个幸存者区当前使用比例
E伊甸园区空间当前使用比例
O老年代空间当前使用比例
M方法区空间当前使用比例
CCSC压缩类空间大小
YGC年轻代垃圾回收次数
YGCT年轻代垃圾回收消耗时间
FGC老年代垃圾回收次数
FGCT老年代垃圾回收消耗时间
GCT垃圾回收总耗时

-gccause : 雷同-gcutil,会额外输出导致最后一次和当前正在发生GC的原因

jstat -gccause 19768
jstat -gccause 19768 1000 10

在这里插入图片描述

参数解析:

参数解析
S0第一个幸存者区当前使用比例
S1第二个幸存者区当前使用比例
E伊甸园区空间当前使用比例
O老年代空间当前使用比例
M方法区空间当前使用比例
CCSC压缩类空间大小
YGC年轻代垃圾回收次数
YGCT年轻代垃圾回收消耗时间
FGC老年代垃圾回收次数
FGCT老年代垃圾回收消耗时间
GCT垃圾回收总耗时
LGCC发生GC的原因

-gcnew : 显示新生代GC的信息

jstat -gcnew 19768
jstat -gcnew 19768 1000 10

在这里插入图片描述

参数解析

参数解析
S0C第一个幸存者区空间大小
S1C第二个幸存者区空间大小
S0U第一个幸存者区已使用空间大小
S1U第二个幸存者区已使用空间大小
TT对象在新生代存活的次数
MTT对象在新生代存活的最大次数
DSS期望幸存区大小
EC伊甸园区空间大小
EU伊甸园区已使用空间大小
YGC年轻代垃圾回收次数
YGCT年轻代垃圾回收消耗时间

-gcnewcapacity :雷同-gcnew,以最大、最小空间的方式显示新生代GC的信息

jstat -gcnewcapacity 19768
jstat -gcnewcapacity 19768 1000 10

在这里插入图片描述

参数解析:

参数解析
NGCMN新生代最小容量
NGCMX新生代最大容量
NGC当前新生代容量
S0CMX第一个幸存者区最大容量
S0C第一个幸存者区空间大小
S1CMX第二个幸存者区最大容量
S1C第二个幸存者区空间大小
ECMX伊甸园区最大容量
EC伊甸园区空间大小
YGC年轻代垃圾回收次数
FGC老年代垃圾回收次数

-gcold :显示老年代的GC信息

jstat -gcold 19768
jstat -gcold 19768 1000 10

在这里插入图片描述

参数解析:

参数解析
MC方法区空间大小
MU方法区已使用空间大小
CCSC压缩类空间大小
CCSU压缩类已使用空间大小
OC老年代空间大小
OU老年代已使用空间大小
YGC年轻代垃圾回收次数
FGC老年代垃圾回收次数
FGCT老年代垃圾回收消耗时间
GCT垃圾回收总耗时

-gcoldcapacity: 雷同gcold,以最大、最小空间的方式显示老年代的GC信息

jstat -gcoldcapacity 19768
jstat -gcoldcapacity 19768 1000 10

在这里插入图片描述

参数解析:

参数解析
OGCMN老年代最小容量
OGCMX老年代最大容量
OGC当前老年代大小
OC老年代空间大小
YGC年轻代垃圾回收次数
FGC老年代垃圾回收次数
FGCT老年代垃圾回收消耗时间
GCT垃圾回收总耗时

-compiler : 显示JIT编译编译过的方法和耗时等信息

jstat -compilation 19768

在这里插入图片描述

-printcompilation: 输出已经被JIT编译的方法

jstat -printcompilation 19768

在这里插入图片描述

  • jinfo

    查看虚拟机配置参数信息,调整虚拟机配置参数

    格式:jinfo [option]

    -flag

    查看具体参数值

    jinfo -flag UseG1GC 19768【查看是否使用了G1收集器,结果为+UseG1GC表示使用了G1收集器,结果为-UseG1GC 表示未使用G1收集器】
    

    在这里插入图片描述

    -flag [+|-]

    修改JVM虚拟机参数,表示添加或减少参数。并非所有的JVM参数都是可以修改的。

    可以使用指令

    java -XX:+PrintFlagsFnal -version | grep managable【linux环境下】
    

    查看哪些jvm参数是可以修改的。

    java -flag +PrintGCDetails 【加入GC打印】
    

    在这里插入图片描述

    参数解释:

    JVM虚拟机并未配置打印GC详情,使用【java -flag +PrintGCDetails】后可以看到,JVM已经将打印GC详情参数配置上去了。

    -flag =

    修改JVM虚拟机参数,表示修改JVM参数值。并非所有的JVM参数都是可以修改的。

    可以通过如下指令查看参数是否可以修改:

    java -XX:+PrintFlagsFnal -version | grep managable【linux环境下】
    
    jinfo -flag HeapDumpPath=D:/ 19768【设置堆快照路径】
    

    在这里插入图片描述

    -flags

    查看JVM虚拟机当前参数

    jinfo -flags 19768
    

    在这里插入图片描述

    -sysprops

    查看系统信息

    jinfo -sysprops 19768
    

    在这里插入图片描述

  • jmap

    用于获取dump文件(堆转储二进制快照文件)、查看java进程内存相关信息,如java堆各个区域使用情况,堆中对象的统计信息、类加载信息等。

    使用格式:

    jmap [option]

    jmap [option] <executable >

    jmap 可选的指令有很多,大多数都是用不到的,下面介绍两个常用的指令

    -dump

    生成java堆转储快照dump文件,添加参数【-dump:live】只保存堆中存活对象。dump文件往往用于分析内存溢出或内存泄漏等问题,dump文件可以使用jvisualVM或jconsole等工具打开并分析其内容。

    jmap -dump:live,format=b,file=test.hprof 19768【导出堆中存活对象的快照文件,并指定存放在当前目录下的text.hprof文件中】
    

    -heap

    输出堆空间的详细信息,包括GC的使用、堆配置信息以及内存使用信息等

    jmap -heap 19768
    

    在这里插入图片描述

  • jhat

    JDK自带的堆分析工具,该工具基本上用不上,我们完全可以使用jmap生成快照文件,在通过jvisualVM、Jconsole、MAT或Jprofile等工具分析堆文件。

  • jstack

    用于生成虚拟机当前时刻的线程快照。生成线程快照的作用可用于定位线程出现长时间停顿的原因。如线程死锁、死循环、请求外部资源导致长时间等待等问题。

    在线程快照中有如下几种状态需要注意:

    1、Deadlock【死锁】

    2、Waiting on condition【等待资源】

    3、Waiting on monitor entry 【等待获取监视器】

    4、Blocked【阻塞】

    格式:

    jstack [option]

    -F

    当正常输出请求不响应时,强制输出

    -l

    显示关于锁的附加信息

    jstack 19768
    

    在这里插入图片描述

3.2 命令行处理JVM问题

以下有些方案参考了一些书籍与视频,可能会有雷同的地方。

3.2.1 调整堆内存

在系统运行过程中,持续监控虚拟机信息,可以通过指令【jstat -gc 19768 1000 100】持续查看GC发生的频率。

在这里插入图片描述

如果FullGC频率异常,会导致用户线程停顿时间增多,影响与用户间的交互性能。FullGC的发生是由于老年代内存不足无法分配更多的内存给对象,可以尝试增大堆内存。

-Xmx1008M -Xms1008M
调整为【增大堆内存】
-Xmx2048M -Xms2048M

如果YGC频率异常,也会导致用户线程停顿时间增多,影响与用户间的交互性能,YGC的发生是由于伊甸园区的内存不足无法分配更多的内存给对象,可以尝试增大新生代内存、增大堆内存或者缩小新生代和老年代的比例。

-Xmx1008M -Xms1008M -Xmn300M
1、调整为【增大新生代内存|增大堆内存】
-Xmx1008M -Xms1008M -Xmn400M
2、调整为【增大堆内存】
-Xmx2048M -Xms2048M
3.2.2 定位内存或CPU占用过高

1、使用指令

top 【按M查看占用内存最高的进程,按C查看CPU占用最高的进程】

在这里插入图片描述

找到java进程PID为2266的进程CPU占用过高

2、获取当前进程中的线程

ps H -eo pid,tid,%cpu| grep <pid>【查看进程中线程列表,输出内容有pid,tid,CPU】

在这里插入图片描述

找到在PId为2266的进程中中2276的线程占用cpu最高

3、查看进程中线程快照信息

在这里插入图片描述

4、将十进制的线程Id转换为十六进制的线程Id,查找线程信息中与线程Id匹对的线程

prinf "%x\n" <tid>

在这里插入图片描述

查出线程Id为2276的十六进制是8e4

5、在线程快照信息中找到线程Id为8e4的线程信息

在这里插入图片描述

6、根据线程信息找到代码,检查代码是否存在异常。

3.2.3 定位内存泄漏

内存泄漏往往指的是那么不用的对象却依旧存活在内存中,比如类变量,类变量的生命的周期与虚拟机等同,但是我们在使用类变量的时候可能只是在某一个方法中使用,其作用范围只在方法范围内,理论上来说,该变量应该定义为方法中局部变量。由于误操作定义成了类变量。这种情况也算是内存泄漏。

如何排查内存泄漏呢?

1、使用指令【jmap -dump:live,format=b,file=test.hprof 19768】生成快照文件,

或者在启动参数中加入【 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc】当系统出现OOM异常时生成快照。

2、使用JvisualVm打开快照文件,在概要中查看出现异常的线程,然后定位异常代码

在这里插入图片描述

3.2.4 检测死锁

​ 使用指令【jstack 19768】生成线程快照,可以从快照中查看到处于死锁状态的线程

在这里插入图片描述

3.3 阿里Arthas

Arthas 可以在线排查问题,不需要重启,动态跟踪Java代码,实时监控JVM状态

3.3.1 下载Arthas工具
wget https://arthas.gitee.io/arthas-boot.jar
3.3.2 启动Arthas
java -jar arthas-boot.jar
# 输入编号,选择进程 如输入【1】

在这里插入图片描述

3.3.3 JVM指令

dashboard

dashboard
dashboard -i 1000 -n 10 【间隔多长时间打印一次,一共打印几次,单位为毫秒,该指令的意思是间隔1000毫秒打印一次,一共10次】

当前系统的实时数据面板

在这里插入图片描述

第一个板块显示的是线程相关的信息

第二个板块显示的是内存相关信息

第三个板块显示的是运行环境信息

thread

查看当前jvm线程列表

thread

在这里插入图片描述

thread 91 【查看指定的线程信息 91为线程号】

在这里插入图片描述

thread -b 【查看阻塞状态的线程,主要用于定位死锁的线程】

在这里插入图片描述

thread -i 5000 【统计5秒中内CPU的利用率的线程】

在这里插入图片描述

thread -n 5 【统计cpu使用率最高的5个线程】

在这里插入图片描述

jvm

查看当前jvm的信息

jvm

在这里插入图片描述

sysprop

查看jvm的系统信息

sysprop

在这里插入图片描述

sysenv

查看jvm环境变量

sysenv

在这里插入图片描述

logger

查看或修改logger,该指令有助于定位问题,如果生产环境的日志级别为INFO,发生异常时,我们可以将日志级别调整为debug级别,以获取更多的异常信息。

logger info -n ROOT【获取系统日志信息】

在这里插入图片描述

logger -c 238e0d81 --name ROOT --level DEBUG【修改日志级别为debug】

在这里插入图片描述

heapdump

生成堆转储快照,类似于jmap命令的heap dump指令功能

heapdump /opt/test.hprof【生成堆快照文件并保存在/opt/test.hprof目录下】
heapdump --live /opt/test.hprof 【生成存活对象堆快照文件并保存在/opt/test.hprof目录下】

在这里插入图片描述

3.3.4 class/classloader相关指令

sc

输出类相关信息

 sc -d java.lang.String 【查看类的详情信息】
 sc -d -f java.lang.String 【查看类的详情信息及变量属性信息】

在这里插入图片描述

sm

输出类方法信息

sm java.lang.String 【输出类方法列表】
sm -d java.lang.String length【输出类方法详情,如输入String类的length方法详情】

在这里插入图片描述

在这里插入图片描述

jad

反编译指定加载类的源码,该指令可以用来确认发布到环境上的代码是否是最新的,也能查看源码是否正确,对线上定位问题有些帮助。

jad java.lang.String【反编译String源码】

在这里插入图片描述

classloader

查看classloader继承树和类加载信息。

classloader 【显示类加载器信息,如加载了多少个类】
classloader -t 【以树形式显示类加载器信息】

在这里插入图片描述

3.3.5 监控相关指令

monitor

方法执行监控

monitor java.lang.String toString 【监控String.toString()方法的执行情况,默认为2分钟】
monitor -c 5 java.lang.String toString 【指定间隔时间,输出String.toString()方式的执行情况】

在这里插入图片描述

watch

方法执行数据监控【注意,该指令不要乱用,可能会导致卡死】

watch java.lang.String toString 【监控方法数据】

在这里插入图片描述

trace

方法内部调用路径,并输出方法路径上每个节点的耗时

trace java.lang.String toString

ng类的length方法详情】


[外链图片转存中...(img-K8YrXL21-1704855707735)]

[外链图片转存中...(img-zISNLinF-1704855707735)]

jad

反编译指定加载类的源码,该指令可以用来确认发布到环境上的代码是否是最新的,也能查看源码是否正确,对线上定位问题有些帮助。

```shell
jad java.lang.String【反编译String源码】

[外链图片转存中…(img-C5MAKd0H-1704855707735)]

classloader

查看classloader继承树和类加载信息。

classloader 【显示类加载器信息,如加载了多少个类】
classloader -t 【以树形式显示类加载器信息】

[外链图片转存中…(img-zAjsFVil-1704855707735)]

3.3.5 监控相关指令

monitor

方法执行监控

monitor java.lang.String toString 【监控String.toString()方法的执行情况,默认为2分钟】
monitor -c 5 java.lang.String toString 【指定间隔时间,输出String.toString()方式的执行情况】

[外链图片转存中…(img-QpSze3wE-1704855707735)]

watch

方法执行数据监控【注意,该指令不要乱用,可能会导致卡死】

watch java.lang.String toString 【监控方法数据】

[外链图片转存中…(img-A19L1dt2-1704855707736)]

trace

方法内部调用路径,并输出方法路径上每个节点的耗时

trace java.lang.String toString
  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值