以下所有测试都是开启了指针压缩(默认)的情况,所以一个指针占4byte。那么,问题来了:
1、现在有10兆长度的数组,问:它占多少内存?(4*10=40兆)
2、10兆长度的引用+10兆长度的数组,问:它占多少内存?(40+160=200兆)
3、一个int+10兆长度的数组+10兆长度的引用,问:它占多少内存?(不变,依然是40+160=200兆)
4、10兆长度的引用里多了一个类B+10兆长度的数组+10兆长度的引用,问:该程序占多少内存?(40+160+160=360兆)
5、一个Integer+10兆长度的数组+10兆长度的引用,问:该程序现在所占内存?(Integer在-127~128内:200兆,这个范围之外:360兆)
先来看一个Demo代码及其运行方法,如果想直接看结论及其原因,可以拉到底下:
1)把我的博客中所列代码新建成一个java文件,如Test.java,示例:
class B{
private int i=5;
}
class A{
B b=new B();
}
public class Test {
public static void main(String[] args) throws InterruptedException {
A[] a=new A[10000000];
for (int i = 0; i < 10000000; i++) {
a[i]=new A();
}
System.out.println("ok");
System.out.flush();
Thread.sleep(999990);
}
}
2)打开控制台,跳转到该java文件目录下,执行Test.java,编译命令为:javac Test.java
然后运行,命令为java Test,当控制台返回ok,说明执行成功
3)打开一个新的控制台,如果你配置了jdk环境变量,可以直接使用这个命令打开jvisualvm。它主要用来监控内存泄露,跟踪垃圾回收,执行时内存、cpu分析,线程分析等等。。。如果对jvisualvm不熟悉,可以点击这里
4)找到左上角的应用程序,双击Test(pid 8556)。
5)执行图中所示步骤,注意:字节除以实例数,就是每一个实例的大小
进入主题:
1、现在有10兆长度的数组,问:它占多少内存?(4*10=40兆)
A[] a = new A[10 000 000]
解释:一个指针占4byte,这个10兆的数组相当于10兆个指针
2、10兆长度的数组+10兆长度的引用,问:它占多少内存?(40+160=200兆)
class A{
}
public class Test {
public static void main(String[] args) throws InterruptedException {
A[] a=new A[10000000];
for (int i = 0; i < 10000000; i++) {
a[i]=new A();
}
System.out.println("ok");
System.out.flush();
Thread.sleep(999990);
}
}
有10兆个new A():在64位jvm下,一个引用占12字节(一个引用包含:一个指向class的指针是4byte,一个对象头markword是8byte),又因为jvm是8字节对齐(8的倍数,不够就会自动填充),所以实际一个引用占16byte。这里有10兆个,所以是10*16=160兆。然后加上数组a的40兆,即可得200兆。
3、10兆长度的数组+10兆长度的引用+一个int,问:它占多少内存?(不变,依然是40+160=200兆)
class A{
// 注意这里,现在是加了1个int,一开始是0个int
private int i1;
}
public class Test {
public static void main(String[] args) throws InterruptedException {
A[] a=new A[10000000];
for (int i = 0; i < 10000000; i++) {
a[i]=new A();
}
System.out.println("ok");
System.out.flush();
Thread.sleep(999990);
}
}
解释:前面说了一个引用占16byte,有12个byte实际使用了,还有4byte是空着的,刚好等于一个int的内存大小,所以0-1个int所占的内存是一样的。
以此类推,2-3个int,8字节对齐所以占160+80=240兆,在该程序下运行所占的内存是40+240=280兆;4-5个int,8字节对齐所以占240+80=320兆,在该程序下运行所占的内存是40+320=360兆
4、10兆长度的引用里多了一个类B+10兆长度的数组+10兆长度的引用,问:该程序占多少内存?(40+160+160=360兆)
class B{
}
class A{
B b=new B();
}
public class Test {
public static void main(String[] args) throws InterruptedException {
A[] a=new A[10000000];
for (int i = 0; i < 10000000; i++) {
a[i]=new A();
}
System.out.println("ok");
System.out.flush();
Thread.sleep(999990);
}
}
解释:类B在64位jvm下实际占120兆,但是因为8字节对齐,所以必须加上40兆空间,得到160兆。
5、一个Integer+10兆长度的数组+10兆长度的引用,问:该程序现在所占内存?(Integer在-127~128内:200兆,这个范围之外:360兆)
class A{
//情况1:小于-127
Integer a=new Integer(-128);
//情况2:-127~128
//Integer a=new Integer(60);
//情况3:大于128的等价于小于-127的
//Integer a=new Integer(129);
}
public class Test {
public static void main(String[] args) throws InterruptedException {
A[] a=new A[10000000];
for (int i = 0; i < 10000000; i++) {
a[i]=new A();
}
System.out.println("ok");
System.out.flush();
Thread.sleep(999990);
}
}
解释:
情况2:Integer的范围在-127~128,所以是存在了缓存里面,相当于int,是40+160=200兆
情况1和情况3一样:范围之外,包装类就是int的引用,一个引用占12个byte,包装类Integer里面存了一个int,所以一个Integer总共占16byte,这里有10兆个,即16*10兆=160兆。那么该程序总共占内存为40+160+160=360兆。
如果想了解java对象头相关信息,可以点击这里