1 生成Dump文件方式
1.1 VisualVM
先启动Java程序并且不能退出,打开VisualVM后就能看到了。如下图,鼠标双击
1.2 通过jmap获取
#获取Java进程
jps -l
#根据jps查看的进程号获取dump文件
jmap -dump:format=b,file='文件路径' '进程号'
1.3 mat
2 浅堆(Shallow heap)和深堆(Retained heap)
2.1 浅堆
指的是对象自身所占据的内存,不包含其内部引用对象的大小
2.2 深堆
只能通过该对象访问到的(直接或者间接)所有对象的浅堆之和,即当对象不再被引用时,垃圾回收器所能回收的总内存,包括对象自身所占据的内存,以及仅能够通过该对象引用到的其他对象所占据的内存。
如下图所示,A的浅堆大小为A本身,由于C被B引用,所有A的深堆大小为A+D;同理可计算B的浅堆大小为B,深堆大小为B+E
3 两种重要视图--直方图(histogram)和支配树(dominator tree)
如下图,是打开这两种视图的方式,可以看到两种视图的界面都会显示Shallow heap列和Retained heap列。
3.1 直方图(histogram)
MAT 的直方图和jmap的-histo子命令一样,都能够展示各个类的实例数目以及这些实例的 Shallow heap 总和。但是,MAT 的直方图还能够计算 Retained heap,并支持基于实例数目或 Retained heap 的排序方式(默认为 Shallow heap)。此外,MAT 还可以将直方图中的类按照超类、类加载器或者包名分组。
3.2 支配树(dominator tree)
在 A支配 B,且 A 不同于 B 的情况下(即 A严格支配 B),如果从 A节点到 B 节点的所有路径中不存在支配 B 的其他节点,那么 A直接支配(immediate dominate)B。这里的支配树指的便是由节点的直接支配节点所组成的树状结构。
支配树基本性质:
- 对象A的子树(所有被对象A支配的对象的集合)表示对象A的保留集(retained set),即深堆
- 如果对象A支配对象B,那么对象A的直接支配者也只支配对象B
- 支配树的边与对象引用图的边不直接对应
下图中,左图表示对象引用图,右图表示左图所对应的支配树。对象A和B由根对象直接支配,由于在到对象C的路径中,可以经过A,也可以经过B,因此对象 C的直接支配者也是根对象。对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此, 对象D是对象F的直接支配者。而到对象D的所有路径中,必然经过对象C,即使是从对象F 到对象D的引用,从根节点出发,也是经过对象C的,所以,对象D的直接支配者为对象C。
4 查看GC Root
打开一个dump文件,按照下图的方式可以查看GC Root列表
下图是执行了main方法的线程,展开后可以看到main方法里有哪些属性作为GC Root
更为详细的解释,可以参考下面这条链接:[https://help.eclipse.org/2020-03/topic/org.eclipse.mat.ui.help/concepts/gcroots.html?cp=59_2_4](https://help.eclipse.org/2020-03/topic/org.eclipse.mat.ui.help/concepts/gcroots.html?cp=59_2_4)
5 查看入引用和出引用
如下图,鼠标右键任意一个对象,选择List objects都会显示 "with outgoing referances" 和 "with incoming referances"两个选项,分别表示出引用(当前对象引用的外部对象)和入引用(引用当前对象的对象)
5.1 示例代码
该程序用于模拟用户访问网站后,将网站的URL记录下来。先初始化100条记录,根据用户id进行取余,条件符合后存入用户历史访问记录集合中。
public class WebPage {
private String url;
private String content;
//get set方法省略
}
public class Student {
private int id;
private String name;
private List history = new Vector();
public Student(int id, String name) {
this.id = id;
this.name = name;
//get set方法省略
}
}
public class TraceStudent {
static List webpages = new Vector<>();
public static void createWebPages(){
for(int i = 0; i < 100; i++){
WebPage wp = new WebPage();
wp.setUrl("http://www."+ Integer.valueOf(i) + ".com");
wp.setContent(Integer.toString(i));
webpages.add(wp);
}
}
public static void main(String[] args) {
createWebPages();
Student st3 = new Student(3,"billy");
Student st5 = new Student(5,"alice");
Student st7 = new Student(7,"taotao");
for(int i = 0; i < webpages.size(); i++){
if(i % st3.getId() == 0){
st3.getHistory().add(webpages.get(i));
}
if(i % st5.getId() == 0){
st5.getHistory().add(webpages.get(i));
}
if(i % st7.getId() == 0){
st7.getHistory().add(webpages.get(i));
}
}
System.gc();
}
}
5.2 运行参数
运行这个程序并使用下面的jvm参数,使得程序退出前能获得堆信息
-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=D:/tmp/tmp/stu.hprof
5.3 查看main方法对应的线程
参照第4小结打开GC Root页面,展开Thread列表,选择main线程,可以看到对应的实例对象
由于0对任意数取余都等于0,所有程序中三个Student对象都存储了这条URL,选择"with incoming referances"查看那些对象引用了下标为0的URL
这里显示了四条,除了3个Student对象外。还有一个用户初始化存储的集合webpages 也引用了,所以总共是4个。
对任意一个Student对象使用"with outgoing referances",可以查看当前对象引用了哪些外部对象
这个功能使用相对较少,因为在支配树(dominator tree)模式下,可以直接看到当前对象引用了哪些外部对象