分析堆
标签:JVM

分析Java堆

1. 浅堆和深堆

浅堆(Shallow Heap): 表示一个对象结构所占用的内存大小

深堆(Retained Heap): 一个对象被GC回收后,可以真实释放的内存大小

1.1 浅堆

浅堆是一个对象所消耗的内存,在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,一个long型变量占8字节,一个对象头占8字节。

如String类型有2个int值共占8字节,对象引用占4字节,对象头8字节,一共20字节,向8字节对齐后,占24字节。这24字节为String对象的浅堆大小,它与String的value实际取值无关,无论字符串长度如何,浅堆始终为24字节。

1.2 深堆

保留集(Retained Set):对象A的保留集指当对象A被垃圾回收后,可以被释放的所有的对象集合(包括对象A本身),即对象A的保留集可以被认为是只能通过对象A直接或间接访问到的所有对象的集合。通俗的说,就是指被对象A所持有的对象的集合。深堆是指对象的保留集中所有的对象的对象的浅堆大小之和。

浅堆指对象本身占用的内存,不包括其内部引用对象的大小。一个对象的深堆指只能通过该对象访问到的(直接或间接)所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。

对象的实际大小指的是一个对象所能触及的所有对象的浅堆大小之和,也就是通常意义上的对象大小,与深堆相比,这个更容易被接受,但实际上与垃圾回收无关。

上面对象A引用了对象C,D,对象B引用了对象C,E。对象的A的浅堆大小为A本身,不含C,D;对象的A的实际大小为A,C,D;而A的深堆大小为A和D之和,不含C,因为C还可以通过B访问到,不在A的深堆范围内。

1.3 测试

Student:

package com.liuyao.heapanalysis;


import java.util.List;
import java.util.Vector;


/**
 * Created By liuyao on 2018/5/6 18:06.
 */
public class Student {
    private int id;
    private String name;
    private List<WebPage> history=new Vector<>();


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void visit(WebPage webPage){
        history.add(webPage);
    }
}

WebPage:

package com.liuyao.heapanalysis;

import java.util.List;
import java.util.Vector;

/**
 * Created By liuyao on 2018/5/6 18:07.
 */
public class WebPage {
    private String url;
    private String content;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

TraceStudent:

package com.liuyao.heapanalysis;

import java.util.List;
import java.util.Vector;

/**
 * Created By liuyao on 2018/5/6 18:08.
 */
public class TraceStudent {
    static List<WebPage> webPages=new Vector<>();
    public static void createWebPages(){
        for (int i = 0; i < 100; i++) {
            WebPage wp=new WebPage();
            wp.setUrl("http://www."+Integer.toString(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,"taobao");
        for (int i = 0; i < webPages.size(); i++) {
            if (i%st3.getId()==0){
                st3.visit(webPages.get(i));
            }
            if (i%st5.getId()==0){
                st5.visit(webPages.get(i));
            }
            if (i%st7.getId()==0){
                st7.visit(webPages.get(i));
            }

        }
        webPages.clear();
        System.gc();
    }
}


在下面的虚拟机参数下运行:

-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=C:/MyFile/stu.hprof

然后使用MAT分析:

在taobao这位同学下,一共有0~14共15条访问记录,96字节的浅堆大小,数组的总长度为20,第15~19项为null,每个引用4字节,合计4*20=80字节,数组对象头8字节,数组长度4字节,合计80+8+4=92字节,向8字节对齐后为96字节。

深堆大小为1304字节,一个13*152+2*144=2264字节,由于0,21,35,42,63,70,84共7个数字(能被3和7整除,能被5和7整除)在前面的两位同学已经访问,这些网站既能被前面的访问,也能被taobao访问,不能算在taobao同学内,故2264-6*152-144=1208字节,再加上浅堆的96字节,一共1208+96=1304字节。

2. 支配树(Dominator Tree)

MAT提供了一个称为支配树的对象图,支配树体现了对象实例之间的支配关系,在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。

支配树是基于对象间的引用图所建立的,它有以下基本性质:

上面的测试例子中taobao同学的支配树,对象支配树中,某一个对象的子树,表示在该对象被回收后,也将被回收的对象集合。

  • 6 min read

CONTRIBUTORS


  • 6 min read