1.常用指令
1.1jps
- jps 是(java process Status Tool), Java版的ps命令,查看java进程及其相关的信息
1.2jinfo【了解】
- jinfo是用来查看JVM参数和动态修改部分JVM参数的命令
- 格式
jinfo [option] <pid>
options参数解释:
- no options 输出所有的系统属性和参数
- -flag 打印指定名称的参数
- -flag [+|-] 打开或关闭参数
- -flag = 设置参数
- -flags 打印所有参数
- -sysprops 打印系统配置
- 例子
package com.lcuyp.jvm.g_tool;
import java.io.IOException;
/**
* jinfo是用来查看JVM参数和动态修改部分JVM参数的命令
*
* jinfo [option] <pid>
* options参数解释:
* - no options 输出所有的系统属性和参数
* - -flag <name> 打印指定名称的参数
* - -flag [+|-]<name> 打开或关闭参数
* - -flag <name>=<value> 设置参数
* - -flags 打印所有参数
* - -sysprops 打印系统配置
*/
public class Demo2_jinfo {
public static void main(String[] args) {
try {
System.out.println("jinfo 指令");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
查看JVM参数和系统配置 :
jinfo 11666
jinfo -flags 11666
jinfo -sysprops 11666
查看打印GC日志参数
jinfo -flag PrintGC 11666
jinfo -flag PrintGCDetails 11666
打开GC日志参数
jinfo -flag +PrintGC 11666
jinfo -flag +PrintGCDetails 11666
关闭GC日志参数
jinfo -flag -PrintGC 11666
jinfo -flag -PrintGCDetails 11666
1.3jstat【重点】
- jstat命令是使用频率比较高的命令,主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等 。
- 命令格式
其中VMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)
jstat [option] pid [interval] [count]
option参数解释:
- -class classloader的行为统计
- -compiler HotSptJIT编译器行为统计
- -gc 垃圾回收堆的行为统计
- -gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
- -gcutil 垃圾回收统计概述,各个区间的容量使用占比
- -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
- -gcnew 新生代行为统计
- -gcnewcapacity 新生代与其相应的内存空间的统计
- -gcold 年老代和永生代行为统计
- -gcoldcapacity 年老代行为统计
- -printcompilation HotSpot编译方法统计
- 例子一: 每隔1s查看垃圾回收堆信息
jstat -gcutil 11666 1000 3
package com.lcuyp.jvm.g_tool;
import java.io.IOException;
/**
* jstat [option] VMID [interval] [count] 查看JVM运行时的状态信息,包括内存状态、垃圾回收等
*
*-class class loader的行为统计
*-gc 垃圾回收堆的行为统计
*-gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
*-gcutil 垃圾回收统计概述
*-gcnew 新生代行为统计
*-gcnewcapacity 新生代与其相应的内存空间的统计
*-gcold 年老代和永生代行为统计
*-gcoldcapacity 年老代行为统计
* 案例:
* jstat -gcutil 11666 1000 3
* jstata -class 11666
* jstata -compiler 11666
*
*/
public class Demo3_jstat {
public static void main(String[] args) {
try {
System.out.println("jstat 指令");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}

S0 survivor0使用百分比
S1 survivor1使用百分比
E Eden区使用百分比
O 老年代使用百分比
M 元数据区使用百分比
CCS 压缩使用百分比
YGC 年轻代垃圾回收次数
YGCT 年轻代垃圾回收消耗时间
FGC Full GC垃圾回收次数
FGCT Full GC垃圾回收消耗时间
GCT 垃圾回收消耗总时间
- 例子二: 查看GC信息
package com.lcuyp.jvm.g_tool;
import java.io.IOException;
/**
* jstat [option] VMID [interval] [count] 查看JVM运行时的状态信息,包括内存状态、垃圾回收等
*
*-class class loader的行为统计
*-gc 垃圾回收堆的行为统计
*-gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
*-gcutil 垃圾回收统计概述
*-gcnew 新生代行为统计
*-gcnewcapacity 新生代与其相应的内存空间的统计
*-gcold 年老代和永生代行为统计
*-gcoldcapacity 年老代行为统计
* 案例:
* jstat -gcutil 11666 1000 3
* jstat -gc 11666 1000 3
*
jvm 参数: -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
*/
public class Demo4_jstat {
public static void main(String[] args) throws IOException {
final int _1M = 1024 * 1024 ;
byte[] b1 = new byte[2 * _1M];
System.out.println("创建b1...");
System.in.read();
byte[] b2 = new byte[2 * _1M];
System.out.println("创建b2...");
System.in.read();
byte[] b3 = new byte[2 * _1M];
System.out.println("创建b3...");
System.in.read();
}
}

- S0C:第一个幸存区的大小,单位KB
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小(元空间)
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间,单位s
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间,单位s
- GCT:垃圾回收消耗总时间,单位s
-gc和-gcutil的区别:-gcutil表示的是占比情况,-gc表示的是实际的值。
1.4jstack【重点】
- jstack是用来查看JVM线程快照的命令,线程快照是当前JVM线程正在执行的方法堆栈集合。使用jstack命令可以定位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack还可以查看程序崩溃时生成的core文件中的stack信息。
- 命令格式
jstack [options] <pid>
- -F 当使用jstack 无响应时,强制输出线程堆栈。
- -m 同时输出java堆栈和c/c++堆栈信息(混合模式)
- -l 除了输出堆栈信息外,还显示关于锁的附加信息
- 例子:查看死锁
package com.lcuyp.jvm.g_tool;
/**
* jstack [options] <pid>
* <p>
* option参数解释:
* - -F 当使用jstack <pid>无响应时,强制输出线程堆栈。
* - -m 同时输出java堆栈和c/c++堆栈信息(混合模式)
* - -l 除了输出堆栈信息外,还显示关于锁的附加信息
*/
public class Demo5_jstack03 {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable {
public void run() {
synchronized (obj1) {
System.out.println("Thread1 拿到了 obj1 的锁!");
try {
// 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("Thread1 拿到了 obj2 的锁!");
}
}
}
}
private static class Thread2 implements Runnable {
public void run() {
synchronized (obj2) {
System.out.println("Thread2 拿到了 obj2 的锁!");
try {
// 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
System.out.println("Thread2 拿到了 obj1 的锁!");
}
}
}
}
}
![]()

1.5jmap【重点】
- jmap可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及finalizer 队列 。
Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和信息保存到文件中
- 格式
jmap [option] 进程pid
- 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始 地址、映射大小以及共享对象文件的路径全称
- -heap 打印java heap摘要
- -histo[:live] 打印堆中的java对象统计信息
- -clstats 打印类加载器统计信息
- -finalizerinfo 打印在f-queue中等待执行finalizer方法的对象
- -dump: 生成java堆的dump文件
- 例子一: 生成java堆的dump文件
jmap -dump:[live],format=b,file=<file-path> <pid>
- live:如果设置,则仅打印具有活动引用的对象,并丢弃准备好进行垃圾回收的对象。此参数是可选的。
- format = b:指定转储文件将采用二进制格式。如果未设置,结果是相同的
- file:将写入的文件
- pid:Java 进程的 ID
package com.lcuyp.jvm.g_tool;
/**
* jmap是用来生成堆dump文件和查看堆相关的各类信息的命令,例如查看finalize执行队列,heap的详细信息和使用情况。
* jmap [option] <pid> (连接正在执行的进程)
*
* 1.打印共享对象
* jmap 11666
*
* 2.打印heap
* jmap -heap 11666
*
* 3.打印存过这对象信息
* jmap -histo:live 9668
*
* 4.查看类加载情况
* jmap -clstats 9668
*
* 5.-finalizerinfo 打印在f-queue中等待执行finalizer方法的对象
* jmap -finalizerinfo 9668
*
* 6.堆转储
* jmap -dump:live,format=b,file=d:/dump.bin 9668
*/
public class Demo6_jmap {
public static void main(String[] args) {
System.out.println("jmap 指令");
try {
Thread.sleep(2000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
用jvisualvm命令工具导入该dump文件分析
jvisualvm

- 打印堆中的java对象统计信息
jmap -histo pid
1.6小结
Q1: 用什么命令查看JVM垃圾回收信息?
jstat
Q2: 死锁怎么检查?
使用jstack命令可以检查出死锁
Q3: 怎么生成 java 程序的 dump 文件?
使用jmap命令或者VisualVM工具
2.VisualVM工具
2.1介绍和插件按照
2.1.1什么是VisualVM
VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于Java 技术的应用程序(Java 应用程序)的详细信息。
2.1.2插件安装
VisualVM基于NetBeans平台开发, 因此它一开始就具备了插件扩展的特性, 通过插件支持, VisualVM可以做许多事情,
例如:
- 显示虚拟机进程和进程的配置、环境信息(jps、jinfo)
- 监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)
- dump及分析堆转储快照(jmap、jhat)
- 方法级的程序运行性能分析, 找出被调用最多、运行时间最长的方法
- 离线程序快照: 收集程序的运行时配置、线程dump、内存dump等信息建立一个快照, 可以将快照发送开发者处
- 进行bug反馈等等
在%JAVA_HOME%\bin目录下, 启动jvisualvm.exe进入主界面, 点击"工具"→"插件"→"可用插件"选项, 选择所需的插件安装。这里安装Visual GC

安装好插件后, 选择一个正在运行的java程序就可以查看程序监控的主界面了

2.1.3小结
Q:什么是VisualVM?
VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于Java 技术的应用程序(Java 应用程序)的详细信息。
2.2操作
2.2.1堆转储快照
两种方式生成堆dump文件:
- 在"应用程序"窗口中右键单击应用程序节点, 选择"堆 Dump"
- 在"监视"页签中选择"堆 Dump"

2.2.2分析程序性能
在Profiler页签中, 可以对程序运行期间方法级的CPU和内存进行分析, 这个操作会对程序运行性能有很大影响, 所以一般不再生产环境使用。CPU分析将会统计每个方法的执行次数、执行耗时; 内存分析则会统计每个方法关联的对象数及对象所占空间 。

package com.lcuyp.jvm.g_tool;
import java.io.IOException;
import java.util.ArrayList;
public class Demo8_JVisualVM {
public static void main(String[] args) throws InterruptedException, IOException {
System.in.read();
System.out.println("fun()执行开始...");
fun();
System.out.println("fun()执行结束...");
System.in.read();
}
private static void fun() throws InterruptedException {
ArrayList<Capacity> list = new ArrayList<Capacity>();
for (int i = 0; i < 10000; i++) {
Thread.sleep(400);
list.add(new Capacity());
}
}
}
class Capacity {
private byte[] big = new byte[8 * 1024 * 1024]; //8m
}
2.2.3例子
- 分析死锁
package com.lcuyp.jvm.g_tool;
public class Deom8_JVisualVM2 {
/**
* 线程死锁等待演示
*/
static class SynAddRunalbe implements Runnable {
int a, b;
public SynAddRunalbe(int a, int b) {
this.a = a;
this.b = b;
}
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Demo7_JConsole03.SynAddRunalbe(1, 2)).start();
new Thread(new Demo7_JConsole03.SynAddRunalbe(2, 1)).start();
}
}
}
- 分析出死锁

2.2.4远程连接jvisualvm【了解】
- 启动普通的jar程序JMX端口配置
java ‐Dcom.sun.management.jmxremote.port=8888‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.management.jmxremot
e.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false ‐jar microservice‐eureka‐server.jar
-
- -Dcom.sun.management.jmxremote.port 为远程机器的JMX端口
- -Djava.rmi.server.hostname 为远程机器IP
- tomcat的JMX配置:在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行
JAVA_OPTS="$JAVA_OPTS ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.ma
nagement.jmxremote.ssl=false‐Dcom.sun.management.jmxremote.authenticate=false"
3.Arthas
3.1介绍
Arthas 是Alibaba开源的Java诊断工具。采用命令行交互模式,可以方便的定位和诊断 线上程序运行问题。
3.2使用
3.2.1下载
- github下载arthas
wget https://alibaba.github.io/arthas/arthas‐boot.jar
- Gitee 下载
wget https://arthas.gitee.io/arthas‐boot.jar

📎arthas-packaging-3.6.7-bin.rar
3.2.2启动
- 启动
jar -jar arthas‐boot.jar

- 如果端口号被占用,也可以通过以下命令换成另一个端口号执行
java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
- Arthas目前支持Web Console,用户在attach成功之后,可以直接访问:http://127.0.0.1:3658/。可以填入IP,远程连接其它机器上的arthas。

默认情况下,arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定listen的IP。
3.2.3常见的使用
3.2.3.1 dashboard
- 作用:显示当前系统的实时数据面板,按q或ctrl+c退出
- 效果:

- ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应
- NAME: 线程名
- GROUP: 线程组名
- PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
- STATE: 线程的状态
- CPU%: 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。
- TIME: 线程运行总时间,数据格式为
分:秒 - INTERRUPTED: 线程当前的中断位状态
- DAEMON: 是否是daemon线程
3.2.3.2thread
- 作用:查看当前 JVM 的线程堆栈信息
- 参数说明
| 参数名称 | 参数说明 |
| 数字 | 线程id |
| [n:] | 指定最忙的前N个线程并打印堆栈 |
| [b] | 找出当前阻塞其他线程的线程 |
| [i <value>] | 指定cpu占比统计的采样间隔,单位为毫秒 |
- 例子一:展示当前最忙的前3个线程并打印堆栈
thread -n 3
- 例子二: 查看是否有死锁(thread -b命令来查看状态是阻塞状态的线程,就会打印出来阻塞线程的堆栈以及阻塞原因、是否有死锁)
thread -b

- 例子三:指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程
thread -i 1000 -n 3
3.2.3.3heapdump
- 作用:dump java heap, 类似 jmap 命令的 heap dump 功能
- 生成dump
heapdump /tmp/dump.hprof
heapdump --live /tmp/dump.hprof #只dump live对象
3.3.4小结
Q1: 什么是arthas?
Arthas 是Alibaba开源的Java诊断工具。采用命令行交互模式,可以方便的定位和诊断 线上程序运行问题。
Q2: 你工作里面JVM调优用什么工具?
- 使用命令+导出dump 使用本地的virtualVM分析
- 使用Arthas
注意: 不能直接使用virtualVM连接远程服务器, 没有权限。
Q3:微服务项目通过打成docker镜像的方式部署, 怎么使用Arthas?
通过dockerfile把Arthas jar添加到微服务的docker镜像里面。
4.GC日志分析
GC日志是一个很重要的工具,它准确记录了每一次的GC的执行时间和执行结果,通过分析GC日志可以优化堆设置和GC设置,或者改进应用程序的对象分配模式。
4.1GC日志参数
不同的垃圾收集器,输出的日志格式各不相同,但也有一些相同的特征。熟悉各个常用垃圾收集器的GC日志,是进行JVM调优的必备一步。 解析GC日志,首先需要收集日志,常用的有以下JVM参数用来打印输出日志信息。
4.1.1GC日志参数
| 参数 | 说明 |
| -XX:+PrintGC | 打印简单GC日志。 类似:-verbose:gc |
| -XX:+PrintGCDetails | 打印GC详细信息 |
| -XX:+PrintGCTimeStamps | 输出GC的时间戳(以基准时间的形式) |
| -XX:+PrintGCDateStamps | 输出GC的时间戳(以日期的形式) |
| -XX:+PrintHeapAtGC | 在进行GC的前后打印出堆的信息 |
| ‐XX:+UseGCLogFileRotation | 开启滚动生成日志 |
| -Xloggc:../logs/gc.log | 指定输出路径收集日志到日志文件 |
- EG:
java ‐jar ‐Xloggc:./gc‐%t.log ‐XX:+PrintGCDetails ‐XX:+PrintGCDateStamps ‐XX:+PrintGCTimeStamps ‐XX:+PrintGCCause ‐XX:+UseGCLogFileRotation ‐XX:NumberOfGCLogFiles=10 ‐XX:GCLogFileSize=100M microservice‐eureka‐server.jar
4.1.2常用垃圾收集器参数
| 参数 | 描述 |
| -XX:+UseSerialGC | 虚拟机在运行在Client 模式下的默认值,打开此开关后,使用Serial+Serial Old 收集器组合进行内存回收 |
| -XX:+UseParNewGC | 使用ParNew + Serial Old 收集器组合进行内存回收 |
| -XX:+UseConcMarkSweepGC | 使用ParNew + CMS + Serial Old 的收集器组合尽心内存回收,当CMS 出现Concurrent Mode Failure 失败后会使用Serial Old 作为备用收集器 |
| -XX:+UseParallelOldGC | 使用Parallel Scavenge + Parallel Old 的收集器组合 |
| -XX:+UseParallelGC | 使用Parallel Scavenge + Serial Old (PS MarkSweep)的收集器组合 |
| -XX:+SurvivorRatio | 新生代中Eden 和任何一个Survivor 区域的容量比值,默认为 8 |
| -XX:+PretenureSizeThreshold | 直接晋升到老年代对象的大小,单位是Byte |
| -XX:+UseAdaptiveSizePolicy | 动态调整Java 堆中各区域的大小以及进入老年代的年龄 |
| -XX:+ParallelGCThreads | 设置并行GC 时进行内存回收的线程数 |
| -XX:+GCTimeRatio | GC 时间占总时间的比率,默认值为99,只在Parallel Scavenge 收集器的时候生效 |
| -XX:+MaxGCPauseMillis | 设置GC 最大的停顿时间,只在Parallel Scavenge 收集器的时候生效 |
| -XX:+CMSInitiatingOccupancyFraction | 设置CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认是68%,仅在CMS 收集器上生效 |
| -XX:+CMSFullGCsBeforeCompaction | 设置CMS 收集器在进行多少次垃圾回收之后启动一次内存碎片整理 |
| -XX:+UseG1GC | 使用G1 (Garbage First) 垃圾收集器 |
| -XX:+MaxGCPauseMillis | 设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(soh goal), JVM 会尽量去达成这个目标. |
| -XX:+G1HeapRegionSize | 使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小. 默认值将根据heap size 算出最优解. 最小值为 1Mb, 最大值为 32Mb. |
4.2GC日志分析
4.2.1日志的含义
GC 日志理解起来十分简单,因为日志本来就是要给开发人员看的,所以设计的很直观。日志例子:
[GC (Allocation Failure) [PSYoungGen: 6146K->904K(9216K)] 6146K->5008K(19456K), 0.0038730 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]
将上面 GC 日志抽象为各个部分,然后我们再分别说明各个部分的含义
[a(b)[c:d->e(f), g secs] h->i(j), k secs] [Times: user:l sys=m, real=n secs]
- a: GC 或者是 Full GC
- b: 用来说明发生这次 GC 的原因
- c: 表示发生GC的区域,这里表示是新生代发生了GC,上面那个例子是因为在新生代中内存不够给新对象分配了,然后触发了 GC
- d: GC 之前该区域已使用的容量
- e: GC 之后该区域已使用的容量
- f: 该内存区域的总容量
- g: 表示该区域这次 GC 使用的时间
- h: 表示 GC 前整个堆的已使用容量
- i: 表示 GC 后整个堆的已使用容量
- j: 表示 Java 堆的总容量
- k: 表示 Java堆 这次 GC 使用的时间
- l: 代表用户态消耗的 CPU 时间
- m: 代表内核态消耗的 CPU 时间
- n: 整个 GC 事件从开始到结束的墙钟时间(Wall Clock Time)
4.3.2使用 ParNew + Serial Old
- 代码
package com.lcuyp.jvm.h_gc;
public class TestGCLog01 {
private static final int _1MB = 1024*1024;
/**
* ParNew + Serial Old
* VM参数:
* 1. -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2*_1MB];
allocation2 = new byte[2*_1MB];
allocation3 = new byte[2*_1MB];
allocation4 = new byte[4*_1MB]; //出现一次 Minor GC
}
public static void main(String[] args) {
testAllocation();
}
}
- 日志
[GC (Allocation Failure) [ParNew: 6250K->666K(9216K), 0.0020346 secs] 6250K->4762K(19456K), 0.0020678 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 7132K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 78% used [0x00000000fec00000, 0x00000000ff250618, 0x00000000ff400000)
from space 1024K, 65% used [0x00000000ff500000, 0x00000000ff5a6b08, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00020, 0x00000000ffa00200, 0x0000000100000000)
Metaspace used 3412K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 374K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
- 结果分析
通过上面的GC日志我们可以看出一开始出现了 MinorGC, 引起GC的原因是 内存分配失败 ,因为分配allocation的时
候,Eden区已经没有足够的区域来分配了,所以发生来本次 MinorGC ,经过 MinorGC 之后新生代的已使用容量从
6146K->753K,然而整个堆的内存总量却几乎没有减少,原因就是,由于发现新生代没有可以回收的对象,所以不
得不使用内存担保将allocation1~3 三个对象提前转移到老年代。此时再在 Eden 区域为 allocation 分配 4MB 的空
间,因此最后我们发现 Eden 区域占用了 4MB,老年代占用了 6MB
4.3.3Parallel Scavenge + Parallel Old
- 代码
package com.lcuyp.jvm.h_gc;
public class TestGCLog02 {
private static final int _1MB = 1024*1024;
/**
* Parallel Scavenge + Parallel Old
* VM参数:
* 1. -Xms20M -Xmx20M -Xmn10M -XX:+UseParallelOldGC -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2*_1MB];
allocation2 = new byte[2*_1MB];
allocation3 = new byte[2*_1MB];
allocation4 = new byte[4*_1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
- 日志
[GC (Allocation Failure) [PSYoungGen: 6250K->872K(9216K)] 6250K->4976K(19456K), 0.0021740 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 7337K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 78% used [0x00000000ff600000,0x00000000ffc50618,0x00000000ffe00000)
from space 1024K, 85% used [0x00000000ffe00000,0x00000000ffeda020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002020,0x00000000ff600000)
Metaspace used 3412K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 374K, capacity 388K, committed 512K, reserved 1048576K
4.3.4大对象回收分析
大对象直接进入老年代 虚拟机提供一个参数 -XX:PretenureSizeThreshold 用来设置直接在老年代分配的对象的大小,如果对象大于这个值就会直接在老年代分配。这样做的目的是避免在 Eden 区及两个Survivor 区之间发生大量的内存复制。
- 代码
package com.lcuyp.jvm.h_gc;
public class TestGCLog03 {
private static final int _1MB = 1024 * 1024;
/**
* VM参数:(参数序号对应实验序号)
* -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=3145728
*/
public static void testPreteureSizeThreshold() {
byte[] allocation;
allocation = new byte[4 * _1MB];
}
public static void main(String[] args) {
testPreteureSizeThreshold();
}
}
- 日志
Heap
par new generation total 9216K, used 2318K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 28% used [0x00000000fec00000, 0x00000000fee43b50, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
Metaspace used 3426K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 375K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
- 结果分析
通过上面的堆的内存占用情况很容易看出我们分配的4MB大小的对象直接被放到了老年代
4.3.5小结
Q: GC日志的含义?
[GC (Allocation Failure) [PSYoungGen: 6146K->904K(9216K)] 6146K->5008K(19456K), 0.0038730 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]
[a(b)[c:d->e(f), g secs] h->i(j), k secs] [Times: user:l sys=m, real=n secs]
- a: GC 或者是 Full GC
- b: 用来说明发生这次 GC 的原因
- c: 表示发生GC的区域,这里表示是新生代发生了GC,上面那个例子是因为在新生代中内存不够给新对象分配了,然后触发了 GC
- d: GC 之前该区域已使用的容量
- e: GC 之后该区域已使用的容量
- f: 该内存区域的总容量
- g: 表示该区域这次 GC 使用的时间
- h: 表示 GC 前整个堆的已使用容量
- i: 表示 GC 后整个堆的已使用容量
- j: 表示 Java 堆的总容量
- k: 表示 Java堆 这次 GC 使用的时间
- l: 代表用户态消耗的 CPU 时间
- m: 代表内核态消耗的 CPU 时间
- n: 整个 GC 事件从开始到结束的墙钟时间(Wall Clock Time)
5日志工具【了解】
5.1简介
GC日志可视化分析工具GCeasy和GCviewer。通过GC日志可视化分析工具,我们可以很方便的看到JVM各个分代的内存使用情况、垃圾回收次数、垃圾回收的原因、垃圾回收占用的时间、吞吐量等,这些指标在我们进行JVM调优的时候是很有用的。
- GCeasy是一款在线的GC日志分析器,可以通过GC日志分析进行内存泄露检测、GC暂停原因分析、JVM配置建议优化等功能,而且是可以免费使用在线分析工具 https://gceasy.io/index.jsp
- GCViewer是一款实用的GC日志分析软件,免费开源使用,你需要安装jdk或者java环境才可以使用。软件为GC日志分析人员提供了强大的功能支持,有利于大大提高分析效率
5.2测试准备
package com.lcuyp.jvm.h_gc;
import java.util.ArrayList;
public class TestGCLog04 {
private static final int _1MB = 1024 * 1024;
/**
* -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -Xloggc:D://gc.log
*/
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<byte[]>();
for (int i = 0; i < 2000; i++) {
byte[] arr = new byte[1024 * 1024];
list.add(arr);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.3GCeasy
- 这是一个在线分析日志的工具,主要功能是免费的,存在部分收费,地址:https://gceasy.io/
5.4GCViewer
- GCViewer是一个小工具,可以可视化展示 生成的详细GC输出。支持Sun / Oracle,IBM,HP和BEA的Java虚拟机。它是GNU LGPL下发布的免费软件。
https://github.com/chewiebug/GCViewer - 下载
https://sourceforge.net/projects/gcviewer/
- 使用
java -jar gcviewer-1.37-SNAPSHOT.jar

被折叠的 条评论
为什么被折叠?



