目录

  • 1,性能优化的步骤
  • 2,常用命令工具
  • 1,jps:查看 Java 进程
  • 2,jstat:查看 JVM 统计信息
  • 3,jinfo:查看修改 JVM 配置参数
  • 4,jmap:导出内存映像文件
  • 5,jhat:分析堆转储文件
  • 6,jstack:查看线程快照
  • 7,jcmd:多功能命令行
  • 3,图形化分析工具
  • 4,JVM 运行时参数
  • 1,JVM 参数选项的类型
  • 2,打印及设置 JVM 参数
  • 3,设置堆、栈、方法区等大小
  • 4,内存溢出相关参数
  • 5,垃圾收集器相关参数
  • 6,GC 日志相关参数
  • 7,其它参数
  • 5,GC 日志分析


1,性能优化的步骤

性能优化步骤:

  • 发现问题-性能监控
  • GC 频繁、CPU 负载过高、OOM、内存泄漏、内存负载过高、死锁、程序响应缓慢
  • jvisualvm、jconsole 等工具
  • 排查问题-性能分析
  • 打印 GC 日志,通过 GCviewer 或者 http://gceasy.io 来分析
  • 灵活使用 jstack、jmap、jinfo 等工具
  • 这些命令的字节码文件都在 C:\Program Files\Java\jdk1.8.0_211\lib\tools.jar
  • 使用阿里 arthas、jconsole、jvisualvm 查看 jvm 状态
  • 生成堆 dump 文件,使用 MemoryAnalyzerJprofilerJconsoleJvisualVmjhat 来分析
  • 解决问题-性能调优
  • 适当增加内存,根据业务背景选择垃圾回收器
  • 优化代码,控制内存使用
  • 增加机器,分散节点压力
  • 合理设置线程池线程数量
  • 等。。

内存泄漏的几种情况:

  • 静态集合类:如 HashMap、LinkedList 等
  • 如果这些容器是静态的,则它们的生命周期与 JVM 程序一致,容器中的对象在程序结束前不能被释放
  • 单例模式:单例的生命周期与 JVM 的生命周期一样长
  • 各种连接:如数据库连接,网络连接、IO 连接等
  • 如果连接没有正常关闭,则造成内存和系统资源浪费
  • 变量不合理的作用域
  • 下图中的 msg 变量只被 receiveMsg 方法用到,可将其移到方法内部
  • 另外字符串及时置 null,也可使字符串及时被回收

2,常用命令工具

1,jps:查看 Java 进程

jps:查看正在运行的 java 进程

  • jps:显示进程 id 与类名
  • jps -l:显示进程 id 与全类名
  • jps -m:显示进程 id 与类名与程序参数
  • jps -v:显示 JVM 参数
  • jps -q:只显示进程 id
  • 如果启动 Java 进程时使用了 -XX:-UserPerfData 参数,那么 jps 将查看不到该进程
2,jstat:查看 JVM 统计信息

jstat:查看 JVM 统计信息,常用于检查垃圾回收与内存泄漏问题

命令格式:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] (使用时要注意参数的顺序)

  • -option
  • -t:输出进程从开始执行到现在的时间(秒)
  • -h<lines>:间隔多少行输出一次标头信息(紧挨着,没空格)
  • vmid:指定进程 id
  • interval:指定输出统计数据的间隔时间,单位毫秒
  • count:指定查询的总次数

查看 option 支持的选项:jstat -options

  • -class:显示 ClassLoader 的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
  • -gc:显示与 GC 相关的堆信息:Eden 区、两个 Survivor 区、老年代、永久代等的容量、已用空间、GC时间合计等信息
  • S0C/S1C:幸存者区大小(单位字节)
  • S0U/S1U:幸存者区已使用的大小
  • EC/EU:Eden区大小 / Eden区已使用的大小
  • OC/OU:老年代大小 / 老年代已使用的大小
  • 如果老年代的使用比例越来越高,而无法降低,则有可能出现内存泄漏
  • MC/MU:方法区大小 / 方法区已使用的大小
  • CCSC/CCSU:压缩类空间大小 / 压缩类已使用的大小
  • YGC/YGCT:进程从启动到目前的 young gc 的次数/消耗时间(秒)
  • FGC/FGCT:进程从启动到目前的 full gc 的次数/消耗时间(秒)
  • GCT:进程从启动到目前的 gc 的消耗时间(秒)
  • 如果 GCT 时间占程序运行时间的 20%,则说明目前堆的压力较大
  • 如果 GCT 时间占程序运行时间的 90%,则随时可能 OOM
  • -gccapacity:显示内容与 -gc 基本相同,但输出主要关注 Java 堆各个区域使用到的最大、最小空间
  • -gcutil:显示内容与 -gc 基本相同,但输出主要关注已使用空间占总空间的百分比
  • -gccause:与 -gcutil 功能一样,但会额外输出导致最后一次或当前正在发生的 GC 产生的原因
  • -gcnew/-gcold:显示新生代/老年代 GC 状况
  • -gcnewcapcity/-gcoldcapacity:与 -gcnew/-gcold 基本相同,输出主要关注使用到的最大、最小空间
  • -gcmetacapacity
  • -compiler:显示 JIT 编译器编译过的方法、耗时等信息
  • -printcompilation:输出已经被 JIT 编译的方法
3,jinfo:查看修改 JVM 配置参数

jinfo:实时查看和修改 JVM 配置参数

并非所有的参数都支持动态修改,只有标记为 manageable 的才能被实时修改

  • java -XX:+PrintFlagsFinal -version | grep manageable
  • java -XX:+PrintFlagsFinal 查看所有 JVM参数的最终值
  • java -XX:+PrintFlagsInitial 查看所有 JVM参数的初始值

jinfo 参数:

  • -sysprops:查看系统属性信息
  • -flags:查看所有赋过值的参数
  • -flag name:查看某个具体的参数的使用用情况
  • -flag +/-name:设置虚拟机参数
  • -flag name=value:设置虚拟机参数
4,jmap:导出内存映像文件

jmap:导出内存映像文件和内存使用情况

基本语法:jmap [option] <pid>

option 选项:

  • -dump:<dump-options>:生成堆转储文件
  • dump:live:只保存堆中的活跃对象
  • dump:fomat=b
  • dump:file=<file_path>
  • -heap:输出整个堆空间的详细信息,包括 GC 的使用,对配置信息,以及内存的使用信息等
  • -histo[:live]:输出堆中对象的同级信息,包括类、实例数量和合计容量
  • -histo:live 只统计堆中的存活对象
  • -permstat:以 ClassLoader 为统计口径输出永久代的内存状态信息
  • -finalizerinfo:显示在 F-Queue 中等待 Finalizer 线程执行 finalize 方法的对象
  • -F:当虚拟机进程对 -dump 选项没有任何响应时,强制生成 dump 文件

导出内存映像文件的两种方式:

  • 使用 jmap:
  • jmap -dump:format=b,file=<file_name.hprof> <pid>
  • jmap -dump:live,format=b,file=<file_name.hprof> <pid> 生成的文件更小,一般情况使用此命令较多
  • 在进程发生 OOM 时,自动生成堆 dump 文件
  • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file_name.hprof>
5,jhat:分析堆转储文件

jhat:堆转储文件分析工具。

  • jhat file_name.hprof

jhat 内置了一个 HTTP 服务器,生成分析结果后,用户可以在浏览器中查看分析结果。jhat 会启动一个 http 服务器,端口是 7000,即 http://localhost:7000,用浏览器访问即可。

该命令在 jdk9 及其之后被移除,官方建议使用 jvisualvm 代替 jhat

6,jstack:查看线程快照

jstack:查看 JVM 中线程快照。常用于分析线程长时间卡顿问题,如线程死锁,死循环,请求外部资源导致长时间等待等问题。

需要关注的几种线程状态:

  • 死锁:Deadlock(重点关注)
  • 等待资源:Waiting on condition(重点关注)
  • 等待获取监视器:Waiting on monitor entry(重点关注)
  • 阻塞:Blocked(重点关注)
  • 执行中:Runnable
  • 暂停:Suspended
  • 对象等待中:TIMED_WATING / WATING
  • 停止:Parked

基本语法jstack [option] <pid>

  • jstack pid :直接查看线程栈信息
  • jstack -F pid :强制输出线程栈信息
  • jstack -l pid :除堆栈外,显示锁的附加信息
  • jstack -m pid :如果调用到本地方法,显示 C/C++ 堆栈信息
7,jcmd:多功能命令行

jcmd 可以用来实现上面除了 jstat 之外所有的功能,比如:导出堆,内存使用,查看进程,导出线程信息,执行 GC,JVM 运行时间等。

命令示例:

# 查看某个 Java 进程的所有支持的命令替换
jcmd <pid> help
---------------
25801:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print					# 打印线程栈
GC.class_stats
GC.class_histogram              # class 直方图
GC.heap_dump					# 导出堆dump文件
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime                       # 显示进程执行的总时间
VM.dynlibs
VM.flags                        # 显示虚拟机参数配置信息
VM.system_properties            # 显示系统属性信息
VM.command_line
VM.version                      # 打印 jdk 版本
help

3,图形化分析工具

JVM 常用图形化分析工具:

  • jdk 自带工具
  • jconsole:基于 JMX
  • jvisualvm
  • jmc
  • 第三方工具
  • MemoryAnalyzer (MAT):主要用于分析堆 dump 文件
  • Jprofiler(商业付费)
  • Arthas(阿里出品-命令行工具)
  • Btrace

4,JVM 运行时参数

1,JVM 参数选项的类型

JVM 参数选项的类型:

  • 标准参数选项:以 - 开头,运行 java -help 可见
  • 参数比较稳定,基本不变动
  • -X 参数选项(非标准参数):以 -X 开头,运行 java -X 可见
  • 参数变动小
  • -Xmixed:JIT 变异模式,混合模式,默认模式
  • -Xint:JIT 变异模式,只是用解释器,所有字节码被解释执行,速度较慢
  • -Xmx:最大堆内存
  • 等价于 -XX:MaxHeapSize
  • 单位 g/G,m/M,k/K
  • -Xms:初始堆内存,最好与 -Xmx一样,避免扩容带来的损耗
  • 等价于 -XX:InitialHeapSize
  • -Xss:线程栈大小
  • 等价于 -XX:ThreadStackSize
  • -XX 参数选项(非标准参数):以 -XX 开头,运行 -XX:PrintFlagsFinal 显示所有参数
  • 实验性,参数变动大
  • 布尔类型:
  • -XX:+<option>:启用某参数
  • -XX:-<option>:禁用某参数
  • 非布尔类型:-XX:<name>=<value>
2,打印及设置 JVM 参数

参数

说明

-XX:+PrintCommandLineFlags

打印用户手动设置或JVM自动设置的参数

-XX:+PrintFlagsInitial

打印所有 XX 选项的默认值

-XX:+PrintFlagsFinal

打印 XX 选项的生效值

-XX:+PrintVMOptions

打印 JVM 参数

3,设置堆、栈、方法区等大小

是GC 执行垃圾回收的重点区域。在方法结束后,堆中的对象不会马上被回收,而是在垃圾收集的时候才会被回收。

运行时数据区结构图:

java jvm性能 jvm性能调优实战_web服务器

java jvm性能 jvm性能调优实战_服务器_02

JVM 堆空间分配:

  • Java 7 及之前:
  • 新生代(Young):Eden、S0(From)、S1(To)
  • 老年代(Old)
  • 永久区(Perm)
  • Java 8 及之后
  • 新生代(Young):IBM研究表明,新生代中的80% 的对象,在经过 GC 之后,都会被回收
  • Eden:对象第一次创建时(new)的区域,如果Eden 放不下,则会直接在 Old 区创建
  • S0(From)
  • S1(To)
  • S0 与 S1 谁空谁是 To
  • 老年代(Old)
  • 元空间(Meta)

堆区中 S1 和 S2 两块区域,同一时刻只会有一块区域使用,另一块是空闲状态。

分代的目的就是为了优化 GC 的性能

java jvm性能 jvm性能调优实战_java jvm性能_03

对象从新生代到老年代的过程:

  • new 的对象先放入 Eden 区
  • 当 Eden 区满,再创建对象时,垃圾回收器将对 Eden 区进行垃圾回收(Minor GC);Eden 区中不再被引用的对象将被销毁,Eden 区中剩余的对象移动到 S0 区。新 new 的对象依然放入 Eden 区
  • 再次发生GC 时,S0 中幸存的对象将放入 S1中,
  • 再次发生GC 时,S1 中幸存的对象将放入 S0中,如此 S0 与 S1 交替往复
  • 当达到一定次数后,对象将被放入 Old 区
  • 该次数可通过参数 -XX:MaxTenuringThreshold 设置,默认 15
  • 当 Old 区空间不足时,会发生 Major GC,清理 Old 区对象
  • Major GC 会比 Minor GC 慢 10 倍以上
  • 若发生 Major GC 后,Old 空间依然不足,将发生 OOM 异常

只有当 Eden 区满了才会触发 Minor GC,Minor GC 会将 Eden 区和 From 区一起回收
如果 To 区满(不会触发 GC),会直接将对象放入 Old 区

java jvm性能 jvm性能调优实战_java_04

发生 GC 时,将 Stop The World

使用 jstat 命令可查看堆中各个区域的空间:

jstat -gc pid

java jvm性能 jvm性能调优实战_web服务器_05

堆空间分配:

区域

大小

比例

新生代

Eden

153600

6

S0(From)

25600

1

S1(To)

25600

1

老年代

Old

409600

16

参数

说明

-Xss/-XX:ThreadStackSize

设置线程栈大小

-Xms/-XX:InitialHeapSize

设置JVM初始堆大小,默认是机器内存的 1/64

-Xmx/-XX:MaxHeapSize

设置JVM最大堆大小,默认是机器内存的 1/4,当实际堆使用量大于此值时,将发生 OOM(一般不设置

-Xmn/-XX:NewSize,-XX:MaxNewSize

设置年轻代初始值/最大值,官方推荐配置为堆大小的3/8一般不设置

-XX:SurvivorRatio

设置年轻代中 Eden区与一个Survior区的比值,默认为 8,8:1:1实际上真正看内存的话是 6:1:1一般不设置

-XX:+UseAdaptiveSizePolicy

自动选择各区大小比例,默认开启 (一般不设置

-XX:NewRatio

设置老年代与年轻代(1个Eden区和2个Survivor区)的比值,默认为 2,2:1一般不设置

-XX:PretenureSizeThreadshould

让大于此值的对象直接分配在老年代,单位字节,只对 Serial、ParNew 收集器有效

-XX:MaxTenuringThreshold

新生代每次MinorGC后,还存活的对象年龄+1,当对象的年龄大于此值时就进入老年代,默认为 15

-XX:+PrintTenuringDistribution

让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布

-XX:TargeSurvivorRatio

设置MinorGC结束后,Survivor 区域中占用空间的期望比例

-XX:PermSize

设置方法区永久代初始值

-XX:MaxPermSize

设置方法区永久代最大值

-XX:MetaspaceSize

设置方法区元空间初始值

-XX:MaxMetaspaceSize

设置方法区元空间最大值

-XX:+UseCompressedOops

使用压缩对象指针

-XX:+UseCompressedClassPointers

是引用压缩类指针

-XX:CompressedClassSpaceSize

设置Klass Metaspace 的大小,默认 1 G

-XX:MaxDirectMemorySize

指定直接内存容量,默认与堆最大值一样

4,内存溢出相关参数

参数

说明

-XX:+HeapDumpOnOutMemoryError

在内存出现OOM时,生成堆转储文件

-XX:+HeapDumpBeforeFullGC

在出现FullGC时,生成堆转储文件,有几次FullGC,就生成几个文件

-XX:HeapDumpPath

设置堆转储文件路径

-XX:OnOutOfMemoryError

设置一个可执行程序或脚本路径,当发生OOM是,执行它

5,垃圾收集器相关参数

java jvm性能 jvm性能调优实战_tomcat_06

Java 垃圾收集器:

  • SerialGC:HotSpot 中 Client模式下的默认新生代垃圾收集器
  • 串行收集器,可获得最高的单线程收集效率
  • SerialOldGC:HotSpot 中 Client模式下的默认老年代垃圾收集器
  • ParNewGC:SerialGC 的并行版本(不重要)
  • ParallelGC:并行收集器,主打吞吐量(jdk 8 默认开启)
  • ParallelOldGC:老年代 GC
  • CMS 收集器:并发收集器,主打低延迟(未来将被丢弃)
  • 使用标记清除算法
  • G1 收集器:主打低延迟

如何选择垃圾收集器:

  • 优先调整堆的大小,让JVM自适应完成
  • 如果内存小于 100M,使用串行收集器
  • 如果是单核,单机程序,且没有停顿时间的要求,使用串行收集器
  • 如果是多核CPU,需要高吞吐量,允许停顿时间超过 1 秒,选择并行或者 JVM 自己选择
  • 如果是多核CPU,追求低停顿时间,需快速响应,使用并发收集器,官方推荐 G1,性能高
  • 现在互联网的项目,基本都是 G1

ParallelGC 垃圾收集器相关参数:

java jvm性能 jvm性能调优实战_tomcat_07

CMS 垃圾收集器相关参数:

java jvm性能 jvm性能调优实战_服务器_08

补充参数:

java jvm性能 jvm性能调优实战_web服务器_09

特别说明:

java jvm性能 jvm性能调优实战_服务器_10

G1 垃圾收集器:

java jvm性能 jvm性能调优实战_java jvm性能_11

java jvm性能 jvm性能调优实战_java_12

6,GC 日志相关参数

参数

说明

-verbose:gc/-XX:+PrintGC

输出GC日志信息

-XX:+PrintGCDetails

在发生垃圾回收时打印内存回收详细日志,并在进程退出时输出当前内存各区域的分配情况

-XX:+PrintGCTimeStamps

程序启动到GC发生的时间秒数,需配合 PrintGCDetails 使用

-XX:+PrintGCDateStamps

输出GC发生时的时间戳,需配合 PrintGCDetails 使用

-XX:+PrintHeapAtGC

每次GC前和GC后,都打印堆信息

-Xloggc:<file>

将GC日志写入文件中

-XX:TraceClassLoading

监控类的加载

-XX:PrintGCApplicationStoppedTime

打印GC时线程的停顿时间

-XX:+PrintGCApplicationConcurrentTime

垃圾收集之前,打印应用未中断的执行时间

-XX:+PrintReferenceGC

记录回收了多少种不同引用类型的引用

-XX:+PrintTenuringDistribution

每次MinorGC后打印出当前使用的Survivor中对象的年龄分布

-XX:+UseGCLogFileRotation

启用GC日志文件的自动转储

-XX:NumberOfGCLogFiles

GC日志文件的循环数目

-XX:GCLogFileSize

控制GC日志文件的大小

7,其它参数

参数

含义

-XX:+DisableExplicitGC

禁用hotspot 执行 System.gc(),默认禁用

-XX:ReservedCodeCacheSize, -XX:InitialCodeCacheSize

指定代码缓存大小

-XX:+UseCodeCacheFlushing

让JVM放弃一些被编译的代码,避免代码缓存被占满时JVM切换到 interpreted-only 的情况

-XX:+DoEscapeAnalysis

开启逃逸分析

-XX:+UseBiasedLocking

开启偏向锁

-XX:+UseLargePages

开启使用大页面

-XX:+PrintTLAB

打印TLAB的使用情况

-XX:TLABSize

设置TLAB大小

5,GC 日志分析

对于 HotSpot VM,它的GC 按照回收区域又分为两大类:

  • 部分收集(Partial GC):非整堆收集,其中又分为:
  • 新生代收集(Minor GC/Young GC):只是新生代(Eden\S0,S1)的垃圾收集
  • 当 Eden 区满的时候就会进行新生代收集
  • 新生代收集比老年代收集更加简单,快速,频繁
  • 老年代收集(Minor GC/Old GC):只是老年代的垃圾收集
  • 目前,只有 CMS GC 会有单独收集老年代的行为
  • 进行老年代收集之前会先进行一次年轻代收集,原因如下:
  • 一个比较大的对象无法放入新生代,那它自然会往老年代去放,如果老年代也放不下,那会先进行一次新生代收集,
  • 之后尝试往新生代放,如果还是放不下,才会进行老年代的垃圾收集,然后往老年代去放
  • 混合收集(Mixed GC):收集整个新生代以及部分老年代
  • 目前,只有 G1 GC 会有这种行为
  • 整堆收集(Full GC):收集整个 java 堆和方法区
  • 堆包含新生代,老年代,元空间/永久代
  • 哪些情况会触发Full GC:
  • 老年代空间不足
  • 方法区空间不足
  • 调用 System.gc()
  • Minor GC 进入老年代的数据的平均大小大于老年代的可用空间
  • 大对象直接进入老年代,而老年代的空间不足