浅堆表示一个对象结构所占用的大小(对象头+实例数据+对齐填充,不包括内部引用 对象大小)
深堆表示一个对象被 GC 回收后,可以真实释放的内存大小(保留空间)
对象
对象 = 对象头 + 实例数据 + 对齐填充
对象头
对象头 = 标记部分 + 原始对象引用
标记部分包括 hashcode、gc 分代年龄、锁状态标志、线程持有锁、偏向线程锁id,偏向时间戳,这一部分在32位机器上为 4 byte,64 位机器上为 8byte
原始对象引用是对象的指针、通过这个指针找到对象的实例、该数据可以压缩,这一部分在 32 位机器上为 4 byte,64位机器上为 8byte,如果开启了压缩(UseCompreddedOops),大小为 4byte,jdk8默认开启压缩
结论:一个对象头大小(64 位) = 12 byte(压缩) / 16 byte(未压缩)
实例数据
对齐填充
任何对象都以 8 byte 来对齐,所以对象的大小为 8 的整数倍
保留内存
只能通过该对象访问的对象浅堆之和(其他对象可以访问到的排除掉)
举例:A,B,C,D,E 5个对象,A 引用 B,C。D 引用C、E
A 的深堆 = A + B + C
A 的保留内存大小 = A +B ,因为 D 引用了 C
例子
实战深堆、浅堆——学生访问网站记录
-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=stu.hprof
WebPage 网站记录
package com.mousycoder.mycode.thinking_in_jvm;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-29 10:38
*/
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;
}
}
Student
package com.mousycoder.mycode.thinking_in_jvm;
import java.util.List;
import java.util.Vector;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-29 10:37
*/
public class Student {
private int id;
private String name;
private List<WebPage> history = new Vector<WebPage>();
public void visit(WebPage webPage){
history.add(webPage);
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
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 List<WebPage> getHistory() {
return history;
}
public void setHistory(List<WebPage> history) {
this.history = history;
}
}
TraceStudent
package com.mousycoder.mycode.thinking_in_jvm;
import java.util.List;
import java.util.Vector;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-29 10:39
*/
public class TraceStudent {
static List<WebPage> webPages = new Vector<WebPage>();
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,"taotao");
for (int i = 0; i < webPages.size(); i++) {
if (i % st3.getId() == 0) {
st3.visit(webPages.get(i)); // 访问能被id 整除的网站
}
if (i % st5.getId() == 0 ) {
st5.visit(webPages.get(i));
}
if (i % st7.getId() == 0) {
st7.visit(webPages.get(i));
}
}
webPages.clear();
System.gc();
}
}
用 MAT 打开stu.hprof 打开线程视图,可以看到每个student实例的 shallow heap 为 24 bytes (2 个 ref 引用 history + name = 4 byte * 2 = 8 byte ,1 个 int 类型 = 4 byte ,标记部分 8 byte ,一共 20 byte,向 8byte 靠齐就是 24 byte)
webpage 的深堆为 152 byte (80 + 48 + 24)
elementData 深堆大小为 96 byte (每个引用 4 个字节,合计 4*20=80 字节,数组对象头 8 个 字节,数组长度 4 个字节 ,合计 80 + 8 + 4 = 92 字节,向 8 字节对齐填充后,为 96 字节)
喜欢的小伙伴可以点点赞,小编在学习过程中整理了一些学习资料,可以分享给做java的工程师朋友们,相互学习,需要的可以关注公众号 l谈java
即可免费获取Java架构学习资料和面试专题及答案!