JVM memory monitoring

1. Overview

1.1. Background

Problems in production environment

  • What should I do if memory overflow occurs in the production environment?
  • How much memory should be allocated to the server in the production environment?
  • How to tune the performance of the garbage collector?
  • How to deal with the high CPU load in the production environment?
  • How many threads should be allocated to the application in the production environment?
  • Without adding a log, how to determine whether a certain line of code is executed by the request?
  • Without adding a log, how to check the input and return value of a method in real time?

Why tune

  • Prevent OOM
  • Solve OOM
  • Reduce the frequency of Full GC

Considerations at different stages

  • Before going live
  • Project operation phase
  • OOM appears online

1.2. Tuning overview

basis for monitoring

  • run log
  • exception stack
  • GC log
  • thread snapshot
  • heap dump snapshot

The general direction of tuning

  • write code sensibly
  • Full and reasonable use of hardware resources
  • Reasonable JVM tuning

1.3. Steps for performance optimization

Step 1: Performance Monitoring

  • GC is frequent
  • cpu load is too high
  • OOM
  • memory leak
  • deadlock
  • Program response time is long

Step 2: Performance Analysis

  • Print the GC log and analyze the abnormal information through GCviewer or Universal JVM GC analyzer - Java Garbage collection log analysis made easy
  • Flexible use of command line tools, jstack, jmap, jinfo, etc.
  • Dump the heap file and use the memory analysis tool to analyze the file
  • Use Ali Arthas, jconsole, JVisualVM to view the JVM status in real time
  • jstack view stack information

Step 3: Performance Tuning

  • Appropriately increase the memory and select the garbage collector according to the business background
  • Optimize code, control memory usage
  • Add machines to disperse node pressure
  • Reasonably set the number of threads in the thread pool
  • Use middleware to improve program efficiency, such as caching, message queues, etc.
  • other……

1.4. Performance evaluation/test indicators

Pause time (or response time)

The time taken between submitting a request and returning a response to that request, generally focusing on the average response time. List of response times for common operations:

operate

Response time

open a site

few seconds

Database query for a record (with index)

ten milliseconds

One-time addressing and positioning of mechanical disk

4 milliseconds

Read 1M data sequentially from the mechanical disk

2 milliseconds

Read 1M data sequentially from SSD disk

0.3 milliseconds

Read a data from remote distribution to Redis

0.5 milliseconds

Read 1M data from memory

ten microseconds

Java program native method call

few microseconds

Network transfer 2Kb data

1 Subtle

During garbage collection:

  • Pause time: The time during which the program's worker threads are suspended while performing garbage collection.
  • -XX:MaxGCPauseMillis

throughput

  • A measure of the amount of work (requests) completed per unit of time
  • In GC: the proportion of events that run user code to the total running time (total running time: program running time + memory recovery time)
  • Throughput is 1-1/(1+n), where -XX::GCTimeRatio=n

concurrent number

  • At the same time, the number of requests for actual interaction with the server

memory usage

  • The memory size occupied by the Java heap area

mutual relationship

Take highway traffic conditions as an example

  • Throughput: the data of vehicles passing the expressway tollbooth every day
  • Concurrent number: the number of vehicles that are driving on the highway
  • Response Time: Vehicle Speed

2. JVM monitoring and diagnostic tools - command line

2.1. Overview

As one of the most popular programming languages, Java's application performance diagnosis has been widely concerned by the industry. There are many factors that may cause performance problems in Java applications, such as thread control, disk read and write, database access, network I/O, garbage collection, and so on. To locate these problems, an excellent performance diagnostic tool is essential.

Experience 1: Use data to explain problems, use knowledge to analyze problems, and use tools to solve problems.

Experience 2: No monitoring, no tuning!

Simple command line tool

When I first came into contact with java learning, the first two commands I learned were javac and java. So besides this, are there any other commands?

Enter the bin directory where jdk is installed, and find that there are a series of auxiliary tools. These auxiliary tools are used to obtain different aspects and different levels of information of the target JVM, helping developers to solve some intractable diseases of Java applications.

Official source address: jdk/jdk11: 1ddf9a99e4ad /src/jdk.jcmd/share/classes/sun/tools/

2.2. jps : View running Java processes

jps(Java Process Status) : Displays all HotSpot virtual machine processes in the specified system (view virtual machine process information), which can be used to query the running virtual machine processes.

Note: For a local virtual machine process, the local virtual machine ID of the process is consistent with the process ID of the operating system and is unique.

The basic usage syntax is:

jps [options] [hostid]

Additional information can be printed by appending parameters.

options parameter

  1. -q: Only display LVMID (local virtual machine id), which is the unique id of the local virtual machine. Do not display the name of the main class, etc.
  2. -l: output the full class name of the main class of the application or if the process executes the jar package, output the full path of the jar
  3. -m: Output the parameters passed to the main class main() when the virtual machine process starts
  4. -v: List the JVM parameters when the virtual machine process starts. For example: -Xms20m -Xmx50m is the jvm parameter specified by the startup program.

Note: The above parameters can be used in combination.

Supplement: If a Java process turns off the default UsePerfData parameter (that is, use the parameter -XX:-UsePerfData ), then the jps command (and the jstat described below) will not be able to detect the Java process.

hostid parameter

The hostname registered in the RMI registry. If you want to remotely monitor the java program on the host, you need to install jstatd.

For sites with more stringent security practices, a custom policy file may be used to show access to specific trusted hosts or networks, although this technique is vulnerable to IP address spoofing attacks.

If security concerns cannot be addressed using a custom policy file, then the safest course of action is not to run the jstatd server, but to use the jstat and jps tools locally.

2.3. jstat : View JVM statistics

jstat (JVM Statistics Monitoring Tool): A command-line tool for monitoring various running status information of virtual machines. It can display running data such as class loading, memory, garbage collection, JIT compilation, etc. in local or remote virtual machine processes. On a server that does not have a GUI graphical interface and only provides a plain text console environment, it will be the preferred tool for locating virtual machine performance problems during runtime. Commonly used to detect garbage collection problems and memory leaks.

Official documentation: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html

The basic usage syntax is:

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

View command-related parameters: jstat-h or jstat-help

Among them, vmid is the process id number, which is the previous number seen after jps, as follows:

 option parameter

The option option can consist of the following values.

Class loading related:

  • -class: Display information about ClassLoader: class loading, unloading quantity, total space, time consumed by class loading, etc.

Garbage collection related:

  • -gc: Display heap information related to GC. Including the Eden area, the two Survivor areas, the capacity of the old generation, the permanent generation, etc., the used space, the total GC time and other information.
  • -gccapacity: The display content is basically the same as -gc, but the output focuses on the maximum and minimum space used by each area of ​​the Java heap.
  • -gcutil: The display content is basically the same as -gc, but the output mainly focuses on the percentage of the used space to the total space.
  • -gccause: Same as -gcutil, but will additionally output the cause of the last or current GC.
  • -gcnew: Display the GC status of the new generation
  • -gcnewcapacity: The display content is basically the same as -gcnew, and the output mainly focuses on the maximum and minimum space used
  • -geold: display the GC status of the old age
  • -gcoldcapacity: The display content is basically the same as -gcold, and the output mainly focuses on the maximum and minimum space used
  • -gcpermcapacity: Displays the maximum and minimum space used by the permanent generation.

JIT-related:

  • -compiler: display the methods compiled by the JIT compiler, time-consuming and other information
  • -printcompilation: Output methods that have been JIT compiled

jstat -class

 jstat -compiler

 jstat -printcompilation

 stand -gc

 jstat -gccacapacity

 stand -gcutil

 jstat -gccause

 stand -gcnew

 jstat -gcnewcapacity

 stand -gcold

 jstat -gcoldcapacity

 stand -t

stand -t -h

Header

EC

The size of the Eden area

EU

The used size of the Eden area

S0C

Survivor 0 size

S1C

size of survivor 1

S0U

The used size of survivor 0

S1U

Survivor 1 area used size

MC

size of metaspace

IN

Metaspace used size

OC

old generation size

OR

used size of the old generation

CCSC

Compressed class space size

CCSU

Compressed class space used size

YGC

The number of young gcs from application startup to sampling

YGCT

Young gc consumption time from application startup to sampling (seconds)

FGC

The number of full gcs from application startup to sampling

FGCT

Full gc consumption time (seconds) from application startup to sampling

GCT

The total time of gc from application start to sample time

interval parameter: used to specify the cycle of output statistics, in milliseconds. That is: query interval

count parameter: used to specify the total number of queries

-t parameter: You can add a Timestamp column before the output information to display the running time of the program. Unit: second

-h parameter: output a table header information after how many rows of data can be output during periodic data output

Supplement: jstat can also be used to determine whether there is a memory leak.

Step 1: In a long-running Java program, we can run the jstat command to continuously obtain multiple rows of performance data, and take the minimum value of the OU column (that is, the occupied old age memory) in these rows of data.

Step 2: Then, we repeat the above operation at intervals of a long period of time to obtain the minimum values ​​of multiple groups of OUs. If these values ​​show an upward trend, it means that the old generation memory usage of the Java program is increasing, which means that objects that cannot be recycled are increasing, so there is a high possibility of memory leaks.

2.4. jinfo : View and modify JVM configuration parameters in real time

jinfo (Configuration Info for Java): View the configuration parameter information of the virtual machine, and can also be used to adjust the configuration parameters of the virtual machine. In many cases, Java applications do not specify all the parameters of the Java virtual machine. At this time, developers may not know the default value of a specific Java virtual machine parameter. In this case, it may be necessary to obtain the default value for a parameter by looking up the documentation. This search process can be very difficult. But with the jinfo tool, developers can easily find the current value of the Java virtual machine parameters.

The basic usage syntax is: jinfo [options] pid

Note: The java process ID must be added

options

Option Description

no option

Export all parameters and system properties

-flag name

Output the parameter corresponding to the name

-flag [+-]name

Turn on or off the parameter with the corresponding name. Only the parameter marked as manageable can be dynamically modified.

-flag name=value

Set the parameter corresponding to the name

-flags

output all parameters

-sysprops

export system properties

jinfo -sysprops

 jinfo -flags

 jinfo -flag

jinfo -flag name

jinfo -flag [+-]name

 expand:

java -XX:+PrintFlagsInitial to view the initial values ​​of all JVM parameter startup

java -XX:+PrintFlagsFinal 查看所有JVM参数的最终值

java -XX:+PrintCommandLineFlags 查看哪些已经被用户或者JVM设置过的详细的XX参数的名称和值

java -XX:+PrintFlagsFinal -version | grep "manageable" 查看可被编辑的JVM参数

2.5. jmap:导出内存映像文件&内存使用情况

jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。开发人员可以在控制台中输入命令“jmap -help”查阅jmap工具的具体使用方式和一些标准选项配置。

官方帮助文档:jmap

基本使用语法为:

  1. jmap [option] <pid>
  2. jmap [option] <executable <core>
  3. jmap [option] [server_id@] <remote server IP or hostname>

选项

作用

-dump

生成dump文件(Java堆转储快照),-dump:live只保存堆中的存活对象

-heap

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

-histo

输出堆空间中对象的统计信息,包括类、实例数量和合计容量,-histo:live只统计堆中的存活对象

-J <flag>

传递参数给jmap启动的jvm

-finalizerinfo

显示在F-Queue中等待Finalizer线程执行finalize方法的对象,仅linux/solaris平台有效

-permstat

以ClassLoader为统计口径输出永久代的内存状态信息,仅linux/solaris平台有效

-F

当虚拟机进程对-dump选项没有任何响应时,强制执行生成dump文件,仅linux/solaris平台有效

说明:这些参数和linux下输入显示的命令多少会有不同,包括也受jdk版本的影响。

 由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。

举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。

另外,如果某个线程长时间无法跑到安全点,jmap将一直等下去。与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。

2.6. jhat:JDK自带堆分析工具

jhat(JVM Heap Analysis Tool):Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。

使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost:7000/,就可以在浏览器里分析。

说明:jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。

基本适用语法:jhat <option> <dumpfile>

option参数

作用

-stack false|true

关闭|打开对象分配调用栈跟踪

-refs false|true

关闭|打开对象引用跟踪

-port port-number

设置jhat HTTP Server的端口号,默认7000

-exclude exclude-file

执行对象查询时需要排除的数据成员

-baseline exclude-file

指定一个基准堆转储

-debug int

设置debug级别

-version

启动后显示版本信息就退出

-J <flag>

传入启动参数,比如-J-Xmx512m

2.7. jstack:打印JVM中线程快照

jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。

生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。

基本语法:jstack [-option] <pid>

官方帮助文档:jstack

在thread dump中,要留意下面几种状态

  1. 死锁,Deadlock(重点关注)
  2. 等待资源,Waiting on condition(重点关注)
  3. 等待获取监视器,Waiting on monitor entry(重点关注)
  4. 阻塞,Blocked(重点关注)
  5. 执行中,Runnable
  6. 暂停,Suspended
  7. 对象等待中,Object.wait() 或 TIMED_WAITING
  8. 停止,Parked

option参数

作用

-F

当正常输出的请求不被响应时,强制输出线程堆栈

-l

除堆栈外,显示关于锁的附加信息

-m

如果调用本地方法的话,可以显示C/C++的堆栈

2.8. jcmd:多功能命令行

在JDK 1.7以后,新增了一个命令行工具jcmd。它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。

官方帮助文档:jcmd

jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令

jcmd -l:列出所有的JVM进程

jcmd 进程号 help:针对指定的进程,列出支持的所有具体命令

jcmd 进程号 具体命令:显示指定进程的指令命令的数据

  • Thread.print 可以替换 jstack指令
  • GC.class_histogram 可以替换 jmap中的-histo操作
  • GC.heap_dump 可以替换 jmap中的-dump操作
  • GC.run 可以查看GC的执行情况
  • VM.uptime 可以查看程序的总执行时间,可以替换jstat指令中的-t操作
  • VM.system_properties 可以替换 jinfo -sysprops 进程id
  • VM.flags 可以获取JVM的配置参数信息

2.9. jstatd:远程主机信息收集

之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat)。为了启用远程监控,则需要配合使用jstatd 工具。命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到远程计算机。

3. JVM监控及诊断工具-GUI篇

3.1. 工具概述

使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限:

  • 1.无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要)。
  • 2.要求用户登录到目标 Java 应用所在的宿主机上,使用起来不是很方便。
  • 3.分析数据通过终端输出,结果展示不够直观。

为此,JDK提供了一些内存泄漏的分析工具,如jconsole,jvisualvm等,用于辅助开发人员定位问题.

JDK自带的工具

  •  jconsole:JDK自带的可视化监控工具。查看Java应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等
  •  Visual VM:Visual VM是一个工具,它提供了一个可视界面,用于查看Java虚拟机上运行的基于Java技术的应用程序的详细信息。
  •  JMC:Java Mission Control,内置Java Flight Recorder。能够以极低的性能开销收集Java虚拟机的性能数据。

第三方工具

  •  MAT:MAT(Memory Analyzer Tool)是基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
  •  JProfiler:商业软件,需要付费。功能强大。

3.2. JConsole

jconsole:从Java5开始,在JDK中自带的java监控和管理控制台。用于对JVM中内存、线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监控工具。

官方地址:Using JConsole - Java SE Monitoring and ManagementGuide

3.3. Visual VM

Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布(VisualVM 在JDK/bin目录下)即:它完全免费。

主要功能:

  • 1.生成/读取堆内存/线程快照
  • 2.查看JVM参数和系统属性
  • 3.查看运行中的虚拟机进程
  • 4.程序资源的实时监控
  • 5.JMX代理连接、远程环境监控、CPU分析和内存分析

官方地址:VisualVM: Home

3.4. Eclipse MAT

MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。是一款免费的性能分析工具,使用起来非常方便。

MAT可以分析heap dump文件。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。一般说来,这些内存信息包含:

  • 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。
  • 所有的类信息,包括classloader、类名称、父类、静态变量等
  • GCRoot到所有的这些对象的引用路径
  • 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)

MAT 不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如Sun,HP,SAP 所采用的 HPROF 二进制堆存储文件,以及 IBM的 PHD 堆存储文件等都能被很好的解析。

最吸引人的还是能够快速为开发人员生成内存泄漏报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉来判断才能发现。

官方地址: Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

3.5. JProfiler

在运行Java的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在IDEA中也有这么一个插件,就是JProfiler。JProfiler 是由 ej-technologies 公司开发的一款 Java 应用性能诊断工具。功能强大,但是收费。

特点:

  • 使用方便、界面操作友好(简单且强大)
  • 对被分析的应用影响小(提供模板)
  • CPU,Thread,Memory分析功能尤其强大
  • 支持对jdbc,noSql,jsp,servlet,socket等进行分析
  • 支持多种模式(离线,在线)的分析
  • 支持监控本地、远程的JVM
  • 跨平台,拥有多种操作系统的安装版本

主要功能:

  • 1-方法调用:对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法
  • 2-内存分配:通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄露问题,优化内存使用
  • 3-线程和锁:JProfiler提供多种针对线程和锁的分析视图助您发现多线程问题
  • 4-高级子系统:许多性能问题都发生在更高的语义级别上。例如,对于JDBC调用,您可能希望找出执行最慢的SQL语句。JProfiler支持对这些子系统进行集成分析

官网地址:Java Profiler - JProfiler

数据采集方式:

JProfier数据采集方式分为两种:Sampling(样本采集)和Instrumentation(重构模式)

Instrumentation:这是JProfiler全功能模式。在class加载之前,JProfier把相关功能代码写入到需要分析的class的bytecode中,对正在运行的jvm有一定影响。

  1. 优点:功能强大。在此设置中,调用堆栈信息是准确的。
  2. 缺点:若要分析的class较多,则对应用的性能影响较大,CPU开销可能很高(取决于Filter的控制)。因此使用此模式一般配合Filter使用,只对特定的类或包进行分析

Sampling:类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。

  1. 优点:对CPU的开销非常低,对应用影响小(即使你不配置任何Filter)
  2. 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)

注:JProfiler本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为JProfiler的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是JProfiler的数据采集类型。

遥感监测 Telemetries

内存视图 Live Memory

Live memory 内存剖析:class/class instance的相关信息。例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。

  • 所有对象 All Objects:显示所有加载的类的列表和在堆上分配的实例数。只有Java 1.5(JVMTI)才会显示此视图。
  • 记录对象 Record Objects:查看特定时间段对象的分配,并记录分配的调用堆栈。
  • 分配访问树 Allocation Call Tree:显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
  • 分配热点 Allocation Hot Spots:显示一个列表,包括方法、类、包或分配已选类的J2EE组件。可以标注当前值并且显示差异值。对于每个热点都可以显示跟踪记录树。
  • 类追踪器 Class Tracker:类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。

堆遍历 heap walker

cpu视图 cpu views

JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。

  • 访问树 Call Tree:显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。
  • 热点 Hot Spots:显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请来进行计算。
  • 访问图 Call Graph:显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。
  • 方法统计 Method Statistis:显示一段时间内记录的方法的调用时间细节。

线程视图 threads

JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。

  • 线程历史 Thread History:显示一个与线程活动和线程状态在一起的活动时间表。
  • 线程监控 Thread Monitor:显示一个列表,包括所有的活动线程以及它们目前的活动状况。
  • 线程转储 Thread Dumps:显示所有线程的堆栈跟踪。

线程分析主要关心三个方面:

  • 1.web容器的线程最大数。比如:Tomcat的线程容量应该略大于最大并发数。
  • 2.线程阻塞
  • 3.线程死锁

监控和锁 Monitors &Locks

所有线程持有锁的情况以及锁的信息。观察JVM的内部线程并查看状态:

  1. 死锁探测图表 Current Locking Graph:显示JVM中的当前死锁图表。
  2. 目前使用的监测器 Current Monitors:显示目前使用的监测器并且包括它们的关联线程。
  3. 锁定历史图表 Locking History Graph:显示记录在JVM中的锁定历史。
  4. 历史检测记录 Monitor History:显示重大的等待事件和阻塞事件的历史记录。
  5. 监控器使用统计 Monitor Usage Statistics:显示分组监测,线程和监测类的统计监测数据

3.6. Arthas

上述工具都必须在服务端项目进程中配置相关的监控参数,然后工具通过远程连接到项目进程,获取相关的数据。这样就会带来一些不便,比如线上环境的网络是隔离的,本地的监控工具根本连不上线上环境。并且类似于Jprofiler这样的商业工具,是需要付费的。

那么有没有一款工具不需要远程连接,也不需要配置监控参数,同时也提供了丰富的性能监控数据呢?

阿里巴巴开源的性能分析神器Arthas应运而生。

Arthas是Alibaba开源的Java诊断工具,在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?
  • 怎么快速定位应用的热点,生成火焰图?

官方地址:快速入门 — Arthas 3.5.5 文档

安装方式:如果速度较慢,可以尝试国内的码云Gitee下载。

wget https://io/arthas/arthas-boot.jar

wget https://arthas/gitee/io/arthas-boot.jar

Arthas只是一个java程序,所以可以直接用java -jar运行。

除了在命令行查看外,Arthas目前还支持 Web Console。在成功启动连接进程之后就已经自动启动,可以直接访问 http://127.0.0.1:8563/ 访问,页面上的操作模式和控制台完全一样。

基础指令

quit/exit 退出当前 Arthas客户端,其他 Arthas喜户端不受影响

stop/shutdown 关闭 Arthas服务端,所有 Arthas客户端全部退出

help 查看命令帮助信息

cat 打印文件内容,和linux里的cat命令类似

echo 打印参数,和linux里的echo命令类似

grep 匹配查找,和linux里的gep命令类似

tee 复制标隹输入到标准输出和指定的文件,和linux里的tee命令类似

pwd 返回当前的工作目录,和linux命令类似

cls 清空当前屏幕区域

session 查看当前会话的信息

reset 重置增强类,将被 Arthas增强过的类全部还原, Arthas服务端关闭时会重置所有增强过的类

version 输出当前目标Java进程所加载的 Arthas版本号

history 打印命令历史

keymap Arthas快捷键列表及自定义快捷键

jvm相关

dashboard 当前系统的实时数据面板

thread 查看当前JVM的线程堆栈信息

jvm 查看当前JVM的信息

sysprop 查看和修改JVM的系统属性

sysem 查看JVM的环境变量

vmoption 查看和修改JVM里诊断相关的option

perfcounter 查看当前JVM的 Perf Counter信息

logger 查看和修改logger

getstatic 查看类的静态属性

ognl 执行ognl表达式

mbean 查看 Mbean的信息

heapdump dump java heap,类似jmap命令的 heap dump功能

class/classloader相关

sc 查看JVM已加载的类信息

    -d 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的Classloader等详细信息。如果一个类被多个Classloader所加载,则会出现多次

    -E 开启正则表达式匹配,默认为通配符匹配

    -f 输出当前类的成员变量信息(需要配合参数-d一起使用)

    -X 指定输出静态变量时属性的遍历深度,默认为0,即直接使用toString输出

sm 查看已加载类的方法信息

    -d 展示每个方法的详细信息

    -E 开启正则表达式匹配,默认为通配符匹配

jad 反编译指定已加载类的源码

mc 内存编译器,内存编译.java文件为.class文件

retransform 加载外部的.class文件, retransform到JVM里

redefine 加载外部的.class文件,redefine到JVM里

dump dump已加载类的byte code到特定目录

classloader 查看classloader的继承树,urts,类加载信息,使用classloader去getResource

    -t 查看classloader的继承树

    -l 按类加载实例查看统计信息

    -c 用classloader对应的hashcode来查看对应的 Jar urls

monitor/watch/trace相关

monitor 方法执行监控,调用次数、执行时间、失败率

-c 统计周期,默认值为120秒

watch 方法执行观测,能观察到的范围为:返回值、抛出异常、入参,通过编写groovy表达式进行对应变量的查看

-b 在方法调用之前观察(默认关闭)

-e 在方法异常之后观察(默认关闭)

-s 在方法返回之后观察(默认关闭)

-f 在方法结束之后(正常返回和异常返回)观察(默认开启)

-x 指定输岀结果的属性遍历深度,默认为0

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

-n 执行次数限制

stack 输出当前方法被调用的调用路径

tt 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

其他

jobs 列出所有job

kill 强制终止任务

fg 将暂停的任务拉到前台执行

bg 将暂停的任务放到后台执行

grep 搜索满足条件的结果

plaintext 将命令的结果去除ANSI颜色

wc 按行统计输出结果

options 查看或设置Arthas全局开关

profiler 使用async-profiler对应用采样,生成火焰图

4. JVM运行时参数

4.1. JVM参数选项

官网地址:java

4.1.1. 类型一:标准参数选项

> java -help

用法: java [-options] class [args...]

           (执行类)

   或  java [-options] -jar jarfile [args...]

           (执行 jar 文件)

其中选项包括:

    -d32          使用 32 位数据模型 (如果可用)

    -d64          使用 64 位数据模型 (如果可用)

    -server       选择 "server" VM

                  默认 VM 是 server.



    -cp <目录和 zip/jar 文件的类搜索路径>

    -classpath <目录和 zip/jar 文件的类搜索路径>

                  用 ; 分隔的目录, JAR 档案

                  和 ZIP 档案列表, 用于搜索类文件。

    -D<名称>=<值>

                  设置系统属性

    -verbose:[class|gc|jni]

                  启用详细输出

    -version      输出产品版本并退出

    -version:<值>

                  警告: 此功能已过时, 将在

                  未来发行版中删除。

                  需要指定的版本才能运行

    -showversion  输出产品版本并继续

    -jre-restrict-search | -no-jre-restrict-search

                  警告: 此功能已过时, 将在

                  未来发行版中删除。

                  在版本搜索中包括/排除用户专用 JRE

    -? -help      输出此帮助消息

    -X            输出非标准选项的帮助

    -ea[:<packagename>...|:<classname>]

    -enableassertions[:<packagename>...|:<classname>]

                  按指定的粒度启用断言

    -da[:<packagename>...|:<classname>]

    -disableassertions[:<packagename>...|:<classname>]

                  禁用具有指定粒度的断言

    -esa | -enablesystemassertions

                  启用系统断言

    -dsa | -disablesystemassertions

                  禁用系统断言

    -agentlib:<libname>[=<选项>]

                  加载本机代理库 <libname>, 例如 -agentlib:hprof

                  另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help

    -agentpath:<pathname>[=<选项>]

                  按完整路径名加载本机代理库

    -javaagent:<jarpath>[=<选项>]

                  加载 Java 编程语言代理, 请参阅 java.lang.instrument

    -splash:<imagepath>

                  使用指定的图像显示启动屏幕

有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation/index.html。

Server模式和Client模式

Hotspot JVM有两种模式,分别是server和client,分别通过-server和-client模式设置

  • 32位系统上,默认使用Client类型的JVM。要想使用Server模式,机器配置至少有2个以上的CPU和2G以上的物理内存。client模式适用于对内存要求较小的桌面应用程序,默认使用Serial串行垃圾收集器
  • 64位系统上,只支持server模式的JVM,适用于需要大内存的应用程序,默认使用并行垃圾收集器

官网地址:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/server-class.html

如何知道系统默认使用的是那种模式呢?

通过java -version命令:可以看到Server VM字样,代表当前系统使用是Server模式

> java -version

java version "1.8.0_201"

Java(TM) SE Runtime Environment (build 1.8.0_201-b09)

Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

4.1.2. 类型二:-X参数选项

> java -X

    -Xmixed           混合模式执行 (默认)

    -Xint             仅解释模式执行

    -Xbootclasspath:<用 ; 分隔的目录和 zip/jar 文件>

                      设置搜索路径以引导类和资源

    -Xbootclasspath/a:<用 ; 分隔的目录和 zip/jar 文件>

                      附加在引导类路径末尾

    -Xbootclasspath/p:<用 ; 分隔的目录和 zip/jar 文件>

                      置于引导类路径之前

    -Xdiag            显示附加诊断消息

    -Xnoclassgc       禁用类垃圾收集

    -Xincgc           启用增量垃圾收集

    -Xloggc:<file>    将 GC 状态记录在文件中 (带时间戳)

    -Xbatch           禁用后台编译

    -Xms<size>        设置初始 Java 堆大小

    -Xmx<size>        设置最大 Java 堆大小

    -Xss<size>        设置 Java 线程堆栈大小

    -Xprof            输出 cpu 配置文件数据

    -Xfuture          启用最严格的检查, 预期将来的默认值

    -Xrs              减少 Java/VM 对操作系统信号的使用 (请参阅文档)

    -Xcheck:jni       对 JNI 函数执行其他检查

    -Xshare:off       不尝试使用共享类数据

    -Xshare:auto      在可能的情况下使用共享类数据 (默认)

    -Xshare:on        要求使用共享类数据, 否则将失败。

    -XshowSettings    显示所有设置并继续

    -XshowSettings:all

                      显示所有设置并继续

    -XshowSettings:vm 显示所有与 vm 相关的设置并继续

    -XshowSettings:properties

                      显示所有属性设置并继续

    -XshowSettings:locale

                      显示所有与区域设置相关的设置并继续

-X 选项是非标准选项, 如有更改, 恕不另行通知。

如何知道JVM默认使用的是混合模式呢?

同样地,通过java -version命令:可以看到 mixed mode 字样,代表当前系统使用的是混合模式

4.1.3. 类型三:-XX参数选项

Boolean类型格式

-XX:+<option>  启用option属性

-XX:-<option>  禁用option属性

非Boolean类型格式

-XX:<option>=<number>  设置option数值,可以带单位如k/K/m/M/g/G

-XX:<option>=<string>  设置option字符值

4.2. 添加JVM参数选项

eclipse和idea中配置不必多说,在Run Configurations中VM Options中配置即可,大同小异

运行jar包

java -Xms100m -Xmx100m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -jar demo.jar

Tomcat运行war包

# linux下catalina.sh添加

JAVA_OPTS="-Xms512M -Xmx1024M"

# windows下catalina.bat添加

set "JAVA_OPTS=-Xms512M -Xmx1024M"

程序运行中

# 设置Boolean类型参数

jinfo -flag [+|-]<name> <pid>

# 设置非Boolean类型参数

jinfo -flag <name>=<value> <pid>

4.3. 常用的JVM参数选项

4.3.1. 打印设置的XX选项及值

-XX:+PrintCommandLineFlags 程序运行时JVM默认设置或用户手动设置的XX选项

-XX:+PrintFlagsInitial 打印所有XX选项的默认值

-XX:+PrintFlagsFinal 打印所有XX选项的实际值

-XX:+PrintVMOptions 打印JVM的参数

4.3.2. 堆、栈、方法区等内存大小设置

# 栈

-Xss128k <==> -XX:ThreadStackSize=128k 设置线程栈的大小为128K



# 堆

-Xms2048m <==> -XX:InitialHeapSize=2048m 设置JVM初始堆内存为2048M

-Xmx2048m <==> -XX:MaxHeapSize=2048m 设置JVM最大堆内存为2048M

-Xmn2g <==> -XX:NewSize=2g -XX:MaxNewSize=2g 设置年轻代大小为2G

-XX:SurvivorRatio=8 设置Eden区与Survivor区的比值,默认为8

-XX:NewRatio=2 设置老年代与年轻代的比例,默认为2

-XX:+UseAdaptiveSizePolicy 设置大小比例自适应,默认开启

-XX:PretenureSizeThreadshold=1024 设置让大于此阈值的对象直接分配在老年代,只对Serial、ParNew收集器有效

-XX:MaxTenuringThreshold=15 设置新生代晋升老年代的年龄限制,默认为15

-XX:TargetSurvivorRatio 设置MinorGC结束后Survivor区占用空间的期望比例



# 方法区

-XX:MetaspaceSize / -XX:PermSize=256m 设置元空间/永久代初始值为256M

-XX:MaxMetaspaceSize / -XX:MaxPermSize=256m 设置元空间/永久代最大值为256M

-XX:+UseCompressedOops 使用压缩对象

-XX:+UseCompressedClassPointers 使用压缩类指针

-XX:CompressedClassSpaceSize 设置Klass Metaspace的大小,默认1G



# 直接内存

-XX:MaxDirectMemorySize 指定DirectMemory容量,默认等于Java堆最大值

4.3.3. OutOfMemory相关的选项

-XX:+HeapDumpOnOutMemoryError 内存出现OOM时生成Heap转储文件,两者互斥

-XX:+HeapDumpBeforeFullGC 出现FullGC时生成Heap转储文件,两者互斥

-XX:HeapDumpPath=<path> 指定heap转储文件的存储路径,默认当前目录

-XX:OnOutOfMemoryError=<path> 指定可行性程序或脚本的路径,当发生OOM时执行脚本

4.3.4. 垃圾收集器相关选项

首先需了解垃圾收集器之间的搭配使用关系

  • 红色虚线表示在jdk8时被Deprecate,jdk9时被删除
  • 绿色虚线表示在jdk14时被Deprecate
  • 绿色虚框表示在jdk9时被Deprecate,jdk14时被删除

# Serial回收器

-XX:+UseSerialGC  年轻代使用Serial GC, 老年代使用Serial Old GC

# ParNew回收器

-XX:+UseParNewGC  年轻代使用ParNew GC

-XX:ParallelGCThreads  设置年轻代并行收集器的线程数。


# Parallel回收器

-XX:+UseParallelGC  年轻代使用 Parallel Scavenge GC,互相激活

-XX:+UseParallelOldGC  老年代使用 Parallel Old GC,互相激活

-XX:ParallelGCThreads

-XX:MaxGCPauseMillis  设置垃圾收集器最大停顿时间(即STW的时间),单位是毫秒。

为了尽可能地把停顿时间控制在MaxGCPauseMills以内,收集器在工作时会调整Java堆大小或者其他一些参数。

对于用户来讲,停顿时间越短体验越好;但是服务器端注重高并发,整体的吞吐量。

所以服务器端适合Parallel,进行控制。该参数使用需谨慎。

-XX:GCTimeRatio  垃圾收集时间占总时间的比例(1 / (N+1)),用于衡量吞吐量的大小

取值范围(0,100),默认值99,也就是垃圾回收时间不超过1%。

与前一个-XX:MaxGCPauseMillis参数有一定矛盾性。暂停时间越长,Radio参数就容易超过设定的比例。

-XX:+UseAdaptiveSizePolicy  设置Parallel Scavenge收集器具有自适应调节策略。

在这种模式下,年轻代的大小、Eden和Survivor的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。

在手动调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆、目标的吞吐量(GCTimeRatio)和停顿时间(MaxGCPauseMills),让虚拟机自己完成调优工作。
# CMS回收器

-XX:+UseConcMarkSweepGC  年轻代使用CMS GC。

开启该参数后会自动将-XX:+UseParNewGC打开。即:ParNew(Young区)+ CMS(Old区)+ Serial Old的组合

-XX:CMSInitiatingOccupanyFraction  设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收。JDK5及以前版本的默认值为68,DK6及以上版本默认值为92%。

如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。

反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。

因此通过该选项便可以有效降低Fu1l GC的执行次数。

-XX:+UseCMSInitiatingOccupancyOnly  是否动态可调,使CMS一直按CMSInitiatingOccupancyFraction设定的值启动

-XX:+UseCMSCompactAtFullCollection  用于指定在执行完Full GC后对内存空间进行压缩整理

以此避免内存碎片的产生。不过由于内存压缩整理过程无法并发执行,所带来的问题就是停顿时间变得更长了。

-XX:CMSFullGCsBeforeCompaction  设置在执行多少次Full GC后对内存空间进行压缩整理。

-XX:ParallelCMSThreads  设置CMS的线程数量。

CMS 默认启动的线程数是(ParallelGCThreads+3)/4,ParallelGCThreads 是年轻代并行收集器的线程数。

当CPU 资源比较紧张时,受到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。

-XX:ConcGCThreads  设置并发垃圾收集的线程数,默认该值是基于ParallelGCThreads计算出来的

-XX:+CMSScavengeBeforeRemark  强制hotspot在cms remark阶段之前做一次minor gc,用于提高remark阶段的速度

-XX:+CMSClassUnloadingEnable  如果有的话,启用回收Perm 区(JDK8之前)

-XX:+CMSParallelInitialEnabled  用于开启CMS initial-mark阶段采用多线程的方式进行标记

用于提高标记速度,在Java8开始已经默认开启

-XX:+CMSParallelRemarkEnabled  用户开启CMS remark阶段采用多线程的方式进行重新标记,默认开启

-XX:+ExplicitGCInvokesConcurrent

-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

这两个参数用户指定hotspot虚拟在执行System.gc()时使用CMS周期

-XX:+CMSPrecleaningEnabled  指定CMS是否需要进行Pre cleaning阶段

# G1回收器

-XX:+UseG1GC 手动指定使用G1收集器执行内存回收任务。

-XX:G1HeapRegionSize 设置每个Region的大小。

值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。默认是堆内存的1/2000。

-XX:MaxGCPauseMillis  设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到)。默认值是200ms

-XX:ParallelGCThread  设置STW时GC线程数的值。最多设置为8

-XX:ConcGCThreads  设置并发标记的线程数。将n设置为并行垃圾回收线程数(ParallelGCThreads)的1/4左右。

-XX:InitiatingHeapOccupancyPercent 设置触发并发GC周期的Java堆占用率阈值。超过此值,就触发GC。默认值是45。

-XX:G1NewSizePercent  新生代占用整个堆内存的最小百分比(默认5%)

-XX:G1MaxNewSizePercent  新生代占用整个堆内存的最大百分比(默认60%)

-XX:G1ReservePercent=10  保留内存区域,防止 to space(Survivor中的to区)溢出

怎么选择垃圾回收器?

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

特别说明:

  • 没有最好的收集器,更没有万能的收集器
  • 调优永远是针对特定场景、特定需求,不存在一劳永逸的收集器

4.3.5. GC日志相关选项

-XX:+PrintGC <==> -verbose:gc  打印简要日志信息

-XX:+PrintGCDetails            打印详细日志信息

-XX:+PrintGCTimeStamps  打印程序启动到GC发生的时间,搭配-XX:+PrintGCDetails使用

-XX:+PrintGCDateStamps  打印GC发生时的时间戳,搭配-XX:+PrintGCDetails使用

-XX:+PrintHeapAtGC  打印GC前后的堆信息,如下图

-Xloggc:<file> 输出GC导指定路径下的文件中

-XX:+TraceClassLoading  监控类的加载

-XX:+PrintGCApplicationStoppedTime  打印GC时线程的停顿时间

-XX:+PrintGCApplicationConcurrentTime  打印垃圾收集之前应用未中断的执行时间

-XX:+PrintReferenceGC 打印回收了多少种不同引用类型的引用

-XX:+PrintTenuringDistribution  打印JVM在每次MinorGC后当前使用的Survivor中对象的年龄分布

-XX:+UseGCLogFileRotation 启用GC日志文件的自动转储

-XX:NumberOfGCLogFiles=1  设置GC日志文件的循环数目

-XX:GCLogFileSize=1M  设置GC日志文件的大小

4.3.6. 其他参数

-XX:+DisableExplicitGC  禁用hotspot执行System.gc(),默认禁用

-XX:ReservedCodeCacheSize=<n>[g|m|k]、-XX:InitialCodeCacheSize=<n>[g|m|k]  指定代码缓存的大小

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

-XX:+DoEscapeAnalysis  开启逃逸分析

-XX:+UseBiasedLocking  开启偏向锁

-XX:+UseLargePages  开启使用大页面

-XX:+PrintTLAB  打印TLAB的使用情况

-XX:TLABSize  设置TLAB大小

4.4. 通过Java代码获取JVM参数

Java提供了java.lang.management包用于监视和管理Java虚拟机和Java运行时中的其他组件,它允许本地或远程监控和管理运行的Java虚拟机。其中ManagementFactory类较为常用,另外Runtime类可获取内存、CPU核数等相关的数据。通过使用这些api,可以监控应用服务器的堆内存使用情况,设置一些阈值进行报警等处理。

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 + "m");

        System.out.println("MAX HEAP: " + usage.getMax() / 1024 / 1024 + "m");

        System.out.println("USE HEAP: " + usage.getUsed() / 1024 / 1024 + "m");

        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");// 最大可用总堆内存大小
    }
}

5. 分析GC日志

5.1. GC分类

针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)

  •  部分收集(Partial GC):不是完整收集整个Java堆的垃圾收集。其中又分为:
    • 新生代收集(Minor GC / Young GC):只是新生代(Eden / S0, S1)的垃圾收集
    • 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。目前,只有CMS GC会有单独收集老年代的行为。
    • 注意,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。
  •  混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。目前,只有G1 GC会有这种行为
  •  整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

5.2. GC日志分类

MinorGC

MinorGC(或young GC或YGC)日志:

[GC (Allocation Failure) [PSYoungGen: 31744K->2192K (36864K) ] 31744K->2200K (121856K), 0.0139308 secs] [Times: user=0.05 sys=0.01, real=0.01 secs]

 

FullGC

[Full GC (Metadata GC Threshold) [PSYoungGen: 5104K->0K (132096K) ] [Par01dGen: 416K->5453K (50176K) ]5520K->5453K (182272K), [Metaspace: 20637K->20637K (1067008K) ], 0.0245883 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]

 

5.3. GC日志结构剖析

透过日志看垃圾收集器

  •  Serial收集器:新生代显示 [DefNew,即 Default New Generation
  •  ParNew收集器:新生代显示 [ParNew,即 Parallel New Generation
  •  Parallel Scavenge收集器:新生代显示[PSYoungGen,JDK1.7使用的即PSYoungGen
  •  Parallel Old收集器:老年代显示[ParoldGen
  •  G1收集器:显示”garbage-first heap“

透过日志看GC原因

  • Allocation Failure:表明本次引起GC的原因是因为新生代中没有足够的区域存放需要分配的数据
  • Metadata GCThreshold:Metaspace区不够用了
  • FErgonomics:JVM自适应调整导致的GC
  • System:调用了System.gc()方法

透过日志看GC前后情况

通过图示,我们可以发现GC日志格式的规律一般都是:GC前内存占用->GC后内存占用(该区域内存总大小)

[PSYoungGen: 5986K->696K (8704K) ] 5986K->704K (9216K)

  •  中括号内:GC回收前年轻代堆大小,回收后大小,(年轻代堆总大小)
  •  括号外:GC回收前年轻代和老年代大小,回收后大小,(年轻代和老年代总大小)

注意:Minor GC堆内存总容量 = 9/10 年轻代 + 老年代。原因是Survivor区只计算from部分,而JVM默认年轻代中Eden区和Survivor区的比例关系,Eden:S0:S1=8:1:1。

透过日志看GC时间

GC日志中有三个时间:user,sys和real

  • user:进程执行用户态代码(核心之外)所使用的时间。这是执行此进程所使用的实际CPU 时间,其他进程和此进程阻塞的时间并不包括在内。在垃圾收集的情况下,表示GC线程执行所使用的 CPU 总时间。
  • sys:进程在内核态消耗的 CPU 时间,即在内核执行系统调用或等待系统事件所使用的CPU 时间
  • real:程序从开始到结束所用的时钟时间。这个时间包括其他进程使用的时间片和进程阻塞的时间(比如等待 I/O 完成)。对于并行gc,这个数字应该接近(用户时间+系统时间)除以垃圾收集器使用的线程数。

由于多核的原因,一般的GC事件中,real time是小于sys time+user time的,因为一般是多个线程并发的去做GC,所以real time是要小于sys+user time的。如果real>sys+user的话,则你的应用可能存在下列问题:IO负载非常重或CPU不够用。

5.4. GC日志分析工具

GCEasy

GCEasy是一款在线的GC日志分析器,可以通过GC日志分析进行内存泄露检测、GC暂停原因分析、JVM配置建议优化等功能,大多数功能是免费的。

官网地址:Universal JVM GC analyzer - Java Garbage collection log analysis made easy

GCViewer

GCViewer is an offline GC log analyzer for visualizing data generated by Java VM options -verbose:gc and .NET -Xloggc:<file>. Performance metrics related to garbage collection can also be calculated (throughput, accumulated pauses, longest pauses, etc.). This feature is useful when tuning garbage collection for a particular application by changing the generation size or setting the initial heap size.

源码下载:GitHub - chewiebug/GCViewer: Fork of tagtraum industries' GCViewer. Tagtraum stopped development in 2008, I aim to improve support for Sun's / Oracle's java 1.6+ garbage collector logs (including G1 collector)

Running version download: Changelog chewebug/GCViewer Wiki GitHub

GChisto

  • There is no place to download on the official website, you need to pull it down from SVN and compile it yourself
  • However, this tool does not seem to be maintained very much, and there are many bugs

HPjmeter

  • The tool is very powerful, but it can only open the GC log generated by the following parameters, -verbose:gc -Xloggc:gc.log. The gc.log generated by adding other parameters cannot be opened
  • HPjmeter integrates the previous HPjtune function to analyze garbage collection log files generated on HP machines

Guess you like

Origin blog.csdn.net/guaituo0129/article/details/122464519