首先,我们对于jvm的一些知识已经都有了一些了解。从内存结构到内存分配的策略,再到类的加载机制,然后就是对于垃圾回收机制的理解,虽然还有很多细节的地方我们不是很了解,但是细节不是一天两天就能完全记住了,要靠我们后面再实践中不断的总结和发现问题。所以我们现在就开始了解下jvm的一些工具的使用,方便我们对jvm进行优化。
JVM参数
首先还是先来看看jvm提供的一些参数,我们有对堆、栈的一些命令有简单的说到。这里我们先大概看一下常用的命令。
![c49c9760dffc54b9d5fd5ad8466f2f56.png](https://i-blog.csdnimg.cn/blog_migrate/865bd887ce6868264ca7a0a07e13c540.jpeg)
这张图中就是我们常用的一些命令。下面简单的用一下。JVM 中最重要的一部分就是堆空间了,基本上大多数的线上 JVM 问题都是因为堆空间造成的 OutOfMemoryError。因此掌握 JVM 关于堆空间的参数配置对于排查线上问题非常重要。
堆配置
我们使用 -Xms 设置堆的初始空间大小,使用 -Xmx 设置堆的最大空间大小。
-Xms20M -Xmx20M -Xmn10M
在上面的命令中,我们设置 JVM 的初始堆大小为 20M,最大堆空间为 20M。然后-Xmn设置年轻代最大内存10M。
年轻代
堆分为年轻代和老年代。JVM 提供了参数 -Xmn 来设置年轻代内存的大小,但没有提供参数设置老年代的大小。但其实老年代的大小就等于堆大小减去年轻代大小。
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
上面的命令中,我们设置 JVM 堆初始大小为20M。其中年轻代的大小为 10M,那么剩下的就是老年代的大小,有 10M了。 我们可以给上述命令加上-XX:+PrintGCDetails
打印GC日志参数来查看内存区域的分配信息。
![e9957d52417a58d858ffb9afc496412d.png](https://i-blog.csdnimg.cn/blog_migrate/b486c9c612de92475a6ac390093454e6.jpeg)
如上图所示,我们可以看到老年代的大小为 10M。
Eden区
在年轻代中,分为三个区域,分别是:eden 空间、from 空间、to 空间。如果要设置这部分的大小,那么就使用 -XX:SurvivorRatio 这个参数,该参数设置 eden / from 空间的比例关系,该参数的公式如下:
-XX:SurvivorRatio = eden/from = eden/to
例如我们的年轻代有 10 M,而我们设置 -XX:SurvivorRatio 参数为 2。也就是说 eden / from = eden / to = 2
。这里教一个快速计算的方法,我们假设 eden = 2,那么 from = 1,to = 1,那么 eden + from + to = 10M。这样就可以算出每一份大小是 10/4 = 2.5M。所以 Eden 区 = 2.5 * 2 = 5M,from 区是 2.5 M,to 区是 2.5 M。
下面我们运行下命令来验证一下。
-Xms20m -Xmn10M -XX:SurvivorRatio=2 -XX:+PrintGCDetails
在上面的启动参数中,我们设置堆初始大小为 20M,年轻代大小为 10M,年轻代的 SurvivorRatio 比例为 2。那么最终分配的结果将会是:年轻代 10M,其中 Eden 区 5M、From 区 2.5M、To 区 2.5 M,老年代 10M。
![3044fde8c61dde49ca287b42dbf1d13f.png](https://i-blog.csdnimg.cn/blog_migrate/dcd52a936106b36ee2905968cb681727.jpeg)
从上图可以看到:eden 空间是 5120 K,from 和 to 空间是 2560 K。
上图还有一个细节,即 PSYoungGen 这里的 total 只有 7680K,难道年轻代只有 7.5M 的内存吗?为什么不是 10M 呢?其实是因为这里的 total 指的是可用内存,from space 和 to space 两个区域,同一时间只有一个区域是可以用的。所以可用内存是 5120 + 2560 = 7680。
永久代
在 JDK 1.8 之前,所加载的类信息都放在永久代中。我们用 -XX:PermSize 设置永久代初始大小,用 -XX:MaxPermSize 设置永久代最大大小。
-XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails
在上面的启动参数中,我们设置永久代初始大小为 10M,最大大小为 50M。我们在 JDK1.7 的环境下运行上面的命令,会看到如下的 GC 日志。
![3f9cbc210c8aeb263229c52b068f9301.png](https://i-blog.csdnimg.cn/blog_migrate/a43c279395ea0360011511e04041daa0.jpeg)
在上图中,我们可以看到永久代的大小为我们设置的 10M。
栈空间
栈空间是每个线程各自有的一块区域,如果栈空间太小,也会导致 StackOverFlow 异常。而要设置栈空间大小,只需要使用 -Xss 参数就可以。
-Xss2m
上面的启动命令设置最大栈空间为 2M。
打印显式参数 -XX:+PrintVMOptions
该参数表示程序运行时,打印虚拟机接受到的命令行显式参数。我们用下面的命令运行程序:
-XX:+UseSerialGC -XX:+PrintVMOptions
输出结果:
Hello World
Disconnected from the target VM, address: '127.0.0.1:26670', transport: 'socket'
VM option '+UseSerialGC'
VM option '+PrintVMOptions'
可以看到我们设置了+UseSerialGC
和+PrintVMOptions
两个参数,最后运行时也将这两个参数打印出来了。
JVM参数之GC日志配置
说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志。而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数。今天就让我们来学习一下 Java 虚拟机中所有与 GC 日志有关的参数。相信掌握了这些参数之后,对于大家线上打印 GC 日志是有不少帮助的。
为了能够更直观地显示出每个参数的作用,我们将以下面的 Demo 为例子去设置 GC 日志参数。
public class GCDemo {
public static void main(String[] args) {
// allocate 4M space
byte[] b = new byte[4 * 1024 * 1024];
System.out.println("first allocate");
// allocate 4M space
b = new byte[4 * 1024 * 1024];
System.out.println("second allocate");
}
}
在上面的程序中,我们两次分配了 4M 的内存空间。为了认为制造 GC,我们启动时的 JVM 参数固定加上下面几个参数:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
- -XX:+UseSerialGC 表示强制使用Serial+SerialOld收集器组合
- -Xms20m 表示堆空间初始大小为 20 M。
- -Xmx20m 表示堆空间最大大小为 20 M。
- -Xmn10m 表示新生代大小为 10M。
- -XX:SurvivorRatio=8 表示Eden:Survivor=8:1
经过上面这个设置,此时我们的堆空间的内存比例情况如下:Eden区 8M,FromSurvivor 1M,ToSurvivor 1M,老年代 10M。
下面就让我们来看看油管 GC 的参数有哪些吧。
打印GC日志
在 GC 日志参数中,最简单的一个参数就是打印 GC 日志:-XX:PrintGC。我们用下面的命令运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGC
输出结果:
first allocate
[GC (Allocation Failure) 6659K->4799K(19456K), 0.0024150 secs]
second allocate
可以看到程序在第一次分配数组空间的时候发生了 GC,并且把 GC 前后以及堆空间大小都打印了出来。该日志显示 GC 前堆空间使用量为 6659K(4M左右)。GC 后堆空间为 4799K,当前可用堆大小为 19456K。
但你会发现使用 PrintGC 参数打印出来的日志比较简单,无法查看更详细的信息。如果你要查看更详细的信息,那么就需要下面这个参数。
打印详细GC日志
如果要查看更加详细的 GC 日志,那么就要使用 -XX:+PrintGCDetails参数。下面我们使用该参数运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails
程序输出:
first allocate
second allocate
[GC (Allocation Failure) [DefNew: 6659K->703K(9216K), 0.0025789 secs] 6659K->4799K(19456K), 0.0026131 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 5039K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 52% used [0x00000000fec00000, 0x00000000ff03c018, 0x00000000ff400000)
from space 1024K, 68% used [0x00000000ff500000, 0x00000000ff5afc40, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
Metaspace used 3327K, capacity 4556K, committed 4864K, reserved 1056768K
class space used 359K, capacity 392K, committed 512K, reserved 1048576K
从上面的日志可以看出,该参数能打印出更加详细的 GC 信息,包括:年轻代的信息、永久代的信息。
[GC (Allocation Failure) [DefNew: 6659K->703K(9216K), 0.0025789 secs] 6659K->4799K(19456K), 0.0026131 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
该参数还会在退出之前打印出整个堆的详细信息:
Heap
def new generation total 9216K, used 5039K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 52% used [0x00000000fec00000, 0x00000000ff03c018, 0x00000000ff400000)
from space 1024K, 68% used [0x00000000ff500000, 0x00000000ff5afc40, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
Metaspace used 3327K, capacity 4556K, committed 4864K, reserved 1056768K
class space used 359K, capacity 392K, committed 512K, reserved 1048576K
GC前后打印堆信息
上面两个命令基本上可以应付 90% 的使用场景了,但有时候我们在 GC 前后还想获取更加详细的信息。那么我们可以使用 PrintHeapAtGC 参数,该参数会在 GC 前后打印堆信息。
使用下面的命令运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintHeapAtGC
输出结果:
first allocate
{Heap before GC invocations=0 (full 0):
def new generation total 9216K, used 6659K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 81% used [0x00000000fec00000, 0x00000000ff280f90, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
Metaspace used 3310K, capacity 4556K, committed 4864K, reserved 1056768K
class space used 358K, capacity 392K, committed 512K, reserved 1048576K
Heap after GC invocations=1 (full 0):
def new generation total 9216K, used 702K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
from space 1024K, 68% used [0x00000000ff500000, 0x00000000ff5afbd0, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
Metaspace used 3310K, capacity 4556K, committed 4864K, reserved 1056768K
class space used 358K, capacity 392K, committed 512K, reserved 1048576K
}
second allocate
仔细看一下,会发现在 GC 发生前后都打印了一次堆空间信息。
![9b232a956203535cbb3b7f8e81dc22ee.png](https://i-blog.csdnimg.cn/blog_migrate/4a3c210c6118aa630e4dbe05a7790bc1.jpeg)
通过这个参数,我们可以详细了解每次 GC 时堆空间的详细信息。
打印GC发生的时间 -XX:+PrintGCTimeStamps
这个参数非常简单,就是在每次 GC 日志的前面加上一个时间戳。这个时间戳表示 JVM 启动后到现在所逝去的时间。
使用下面的参数运行程序:
java -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGC -XX:+PrintGCTimeStamps com.chenshuyi.GCDemo
输出结果:
first allocate
second allocate
0.130: [GC (Allocation Failure) 4767K->4374K(19456K), 0.0051351 secs]
上面日志第 3 行中的「0.130」就是该 GC 发生的时间。
-XX:+PrintGCApplicationConcurrentTime 打印应用程序的执行时间
使用下面的命令运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGC -XX:+PrintGCApplicationConcurrentTime
运行结果:
Application time: 0.0000283 seconds
Application time: 0.0570958 seconds
Application time: 0.0004787 seconds
first allocate
Application time: 0.0029640 seconds
[GC (Allocation Failure) 6659K->4799K(19456K), 0.0025556 secs]
second allocate
Application time: 0.0010957 seconds
-XX:+PrintGCApplicationStoppedTime 打印应用由于GC而产生的停顿时间
使用下面的命令运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime
运行结果:
first allocate
second allocate
[GC (Allocation Failure) 4767K->4374K(19456K), 0.0045644 secs]
Total time for which application threads were stopped: 0.0047873 seconds, Stopping threads took: 0.0000329 seconds
可以看到最后一行打印出了因为 GC 而暂停的时间。
保存GC日志 -Xloggc
这个参数可以将 GC 日志输出到文件中保存起来。-XX:+PrintReferenceGC
参数可以记录回收了多少不同引用类型的引用。
使用下面的参数运行程序:
-XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGC -XX:+PrintReferenceGC -Xloggc:gc.log
运行之后在本目录会生成一个 gc.log 文件,打开该文件:
Java HotSpot(TM) 64-Bit Server VM (25.181-b13) for bsd-amd64 JRE (1.8.0_181-b13), built on Jul 7 2018 01:02:31 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Memory: 4k page, physical 8388608k(45132k free)
/proc/meminfo:
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:NewSize=10485760 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC
0.124: [GC (Allocation Failure) 4767K->4374K(19456K), 0.0047748 secs]
可以看到堆的相关信息,以及 GC 的信息。
最后用列表的形式总结一下,加深一下印象。
![b808e1a4573197fe0d5d0aab9e342bf5.png](https://i-blog.csdnimg.cn/blog_migrate/6c1a336e3800806f068f7c0da1385d67.jpeg)
常用工具
查看虚拟机进程:jps 命令
jps 命令可以列出所有的 Java 进程。如果 jps 不加任何参数,可以列出 Java 程序的进程 ID 以及 Main 函数短名称,如下所示。
$ jps
6540 Jps
64447 Main
除此之外,还可以指定下面的参数自定义输出信息:
![22803a18ceb3dc8b8fdb7bd63e1aaa67.png](https://i-blog.csdnimg.cn/blog_migrate/914131d9e7f84a95cd5a7a10936aff61.png)
虚拟机统计信息:jstat 命令
jstat 用于观察 Java 堆信息的详细情况,其基本使用语法位:
jstat -<option> [-t] [-h<lines>] <vmid> [<interval>] [<count>]]
其中 option 可以由以下值构成。
![b24b6b51822914e81434bb0332172764.png](https://i-blog.csdnimg.cn/blog_migrate/3947b5416e76041f9cb96c8114c4311b.jpeg)
-t 参数表示输出时间戳、-h 参数表示在多少行后输出一个表头、vmid 则是虚拟机的进程ID、interval 和 count 表示输出间隔以及输出次数。
例如:我们用jstat命令来查看一个8644的JVM进程的gc信息。
jstat -gc 8644 1000 10
![4f1c670aafe8c2eadf3d3bcf2be20b88.png](https://i-blog.csdnimg.cn/blog_migrate/69153ff07d503a9a4abc0b3fb02e5182.jpeg)
查看虚拟机参数:jinfo 命令
jinfo 可以用来查看正在运行的 Java 应用程序的扩展参数,甚至支持在运行时,修改部分参数。它的基本语法是:
jinfo <option> <pid>
执行例子,查询 MaxHeapSize 参数值
C:Users1002360>jinfo -flag MaxHeapSize 8644
-XX:MaxHeapSize=4280287232
查看线程堆栈:jstack 命令
jstack 命令用于导出 Java 应用程序的线程堆栈。jstack命令格式:
jstack [option] vmid
下面使用jstack查看一个线程堆栈的例子:
![8a9d43befd47ac0da35d0b32cc3b5921.png](https://i-blog.csdnimg.cn/blog_migrate/062e08f5d672bde154e9471819e10015.jpeg)
使用jstack排查死锁:
demo:
//运行主类
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock d1 = new DeadLock(true);
DeadLock2 d2 = new DeadLock2(true);
Thread t1 = new Thread(d1);
Thread t2 = new Thread(d2);
t1.start();
t2.start();
}
}
//定义锁对象
class MyLock {
public static Object obj1 = new Object();
public static Object obj2 = new Object();
}
//死锁代码
class DeadLock implements Runnable {
private boolean flag;
DeadLock(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
while (true) {
synchronized (MyLock.obj1) {
System.out.println(Thread.currentThread().getName() + "----if获得obj1锁");
synchronized (MyLock.obj2) {
System.out.println(Thread.currentThread().getName() + "----if获得obj2锁");
}
}
}
}
}
}
class DeadLock2 implements Runnable {
private boolean flag;
DeadLock2(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
while (true) {
synchronized (MyLock.obj2) {
System.out.println(Thread.currentThread().getName() + "----if获得obj1锁");
synchronized (MyLock.obj1) {
System.out.println(Thread.currentThread().getName() + "----if获得obj2锁");
}
}
}
}
}
}
拉到最后发现:
![dc148e0a6259d2ac4c408653b4395fca.png](https://i-blog.csdnimg.cn/blog_migrate/cf9d34b89dd3e091c23adc1d1649e46b.jpeg)
导出堆到文件:jmap 命令
jmap 是一个多功能命令,可以生成 Java 程序的 Dump 文件,也可以查看堆内对象实例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。
jmap [option] vmid
打印堆内存信息
-XX:+PrintFlagsFinal -Xms300M -Xmx300M
jmap -heap PID
![da999d13c5beaea0d34610386b204611.png](https://i-blog.csdnimg.cn/blog_migrate/fd0271eaca2f8a77f044b2ded72f6047.jpeg)
dump出堆内存相关信息:
jmap -dump:format=b,file=heap.hprof PID
![c6fa422d807f970a31b16ac1eec02438.png](https://i-blog.csdnimg.cn/blog_migrate/cfd141f55f28bb55783d4d614d7b9015.png)
要是在发生堆内存溢出的时候,能自动dump出该日志就好了。所以一般在开发中,JVM参数可以加上两句配置,这样在堆内存溢出的时候,会自动dump出该文件。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/heap.hprof
HeapDumpOnOutOfMemoryError 参数表示当JVM发生OOM时,自动生成DUMP文件。
HeapDumpPath 表示dump文件路径。
关于dump下来的文件,我们需要结合工具进行分析,这块我们后面会说到使用哪些诶工具。
常用分析工具
jconsole
JConsole工具是JDK自带的可视化监控工具。查看java应用程序的运行概况、监控堆信息、永久区使用情况、类加载情况等。
![b82576bc1729301f50f43599fff8843e.png](https://i-blog.csdnimg.cn/blog_migrate/ab5c1b95b5ccd2d3a960643759eea45b.jpeg)
jvisualvm
是jvm自带的工具,可以直接拿到线程dump信息。
![92e2646aab15786f3bbb1498b2aba002.png](https://i-blog.csdnimg.cn/blog_migrate/fc03420014d3dc35bfc0c0b97cd6f89f.jpeg)
监控本地java进程:可以直接打开检测到本地进程
监控远程java进程:本机的VisualVM就必须和远程的JVM要进行通信, Visualvm目前支持两种remote connection方式,分别是jstatd和JMX方式。远程监控某个中间件时,需要修改中间件的启动文件,添加上关于jmx等的信息。
Arthas
Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+, 采用命令行交互模式,提供 Tab 自动不全,可以方便的定位和诊断线上程序运行问题。
得益于 Arthas 强大且丰富的功能,让 Arthas 能做的事情超乎想象。下面仅仅列举几项常见的使用情况,更多的使用场景可以在熟悉了 Arthas 之后自行探索。
是否有一个全局视角来查看系统的运行状况?
为什么 CPU 又升高了,到底是哪里占用了 CPU ?
运行的多线程有死锁吗?有阻塞吗?
程序运行耗时很长,是哪里耗时比较长呢?如何监测呢?
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
有什么办法可以监控到 JVM 的实时运行状态?
- 安装
可以在官方 Github 上进行下载,如果速度较慢,可以尝试国内的码云 Gitee 下载。
# github下载
wget https:// alibaba.github.io/artha s/arthas-boot.jar
- 打印帮助信息
java -jar arthas-boot.jar -h
- 运行
Arthas 只是一个 java 程序,所以可以直接用 java -jar 运行。运行时或者运行之后要选择要监测的 Java 进程。
# 运行方式1,先运行,在选择 Java 进程 PID
java -jar arthas-boot.jar
# 运行方式2,运行时选择 Java 进程 PID
java -jar arthas-boot.jar [PID]
查看 PID 的方式可以通过 ps 命令,也可以通过 JDK 提供的 jps命令。
- 常用命令:
![3fecd7fade9a10898148c7da7d538394.png](https://i-blog.csdnimg.cn/blog_migrate/2777879b1ae55e8570090b5063a05afb.png)
MAT
Java堆分析器,用于查找内存泄漏。Heap Dump,称为堆转储文件,是Java进程在某个时间内的快照。
- dump文件包含的内容:
1,全部的对象:类,域,原生值和引用;
2,全部的类:classloader,类名,超类,静态域;
3,GC root:被JVM定义的可触达的对象;
4,线程栈和本地变量:线程的call stack,本地对象每帧的信息。
- 获取dump文件,可以通过jmap获取,或者jvisualvm工具获取。
- 使用
先写一段内存泄露直到溢出代码并运行
/**
* 内存泄露直到溢出
*/
public class DumpDemo {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
System.out.println("-------------mytime----------------");
Map<String,Object> m = new HashMap<>();
int i = 0;
do{
byte[] test = new byte[_1MB];
m.put(String.valueOf(i), test);
i++;
}while(i<100000);
}
}
![3868940d62e17b2cf2b25c4b2d417f2e.png](https://i-blog.csdnimg.cn/blog_migrate/dd34acd227e0bbd8fbc6a06287d07f11.png)
把dump文件弄下来后导入到MAT中进行分析。
![8f1e026c4c56674e26afe158a09f820c.png](https://i-blog.csdnimg.cn/blog_migrate/9904fe255b4cc1324b810fab616b574a.jpeg)
具体的使用可以进行百度。常用功能:
Histogram
Histogram可以列出内存中的对象,对象的个数及其大小
Class Name:类名称,java类名Objects:类的对象的数量,这个对象被创建了多少个
Shallow Heap:一个对象内存的消耗大小,不包含对其他对象的引用
Retained Heap:是shallow Heap的总和,即该对象被GC之后所能回收到内存的总和
功能:
1:右击类名--->List Objects--->with incoming references--->列出该类的实例
2:右击Java对象名--->Merge Shortest Paths to GC Roots--->exclude all ...--->找到GCRoot以及原因
Leak Suspects
查找并分析内存泄漏的可能原因
使用方式:Reports--->Leak Suspects--->Detail
Top Consumers
列出大对象
GC日志分析工具
要想分析日志的信息,得先拿到GC日志文件才行,所以得先配置一下根据前面参数的学习。下面的配置很容易看懂
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps-Xloggc:gc.log
- 在线分析
http://gceasy.io
- GCViewer工具