浅堆表示一个对象结构所占用的大小(对象头+实例数据+对齐填充,不包括内部引用 对象大小)
深堆表示一个对象被 GC 回收后,可以真实释放的内存大小(保留空间)
对象 = 对象头 + 实例数据 + 对齐填充
对象头 = 标记部分 + 原始对象引用
标记部分包括 hashcode、gc 分代年龄、锁状态标志、线程持有锁、偏向线程锁id,偏向时间戳,这一部分在32位机器上为 4 byte,64 位机器上为 8byte
原始对象引用是对象的指针、通过这个指针找到对象的实例、该数据可以压缩,这一部分在 32 位机器上为 4 byte,64位机器上为 8byte,如果开启了压缩(UseCompreddedOops),大小为 4byte,jdk8默认开启压缩
结论:一个对象头大小(64 位) = 12 byte(压缩) / 16 byte(未压缩)
类型 | 大小(byte) |
---|---|
byte | 1 |
boolean | 1 |
char | 2 |
short | 2 |
int | 4 |
float | 4 |
double | 8 |
long | 8 |
任何对象都以 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 字节)