深入剖析java引用及其变量所占实际内存

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36688143/article/details/84984699

以下所有测试都是开启了指针压缩(默认)的情况,所以一个指针占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对象头相关信息,可以点击这里

猜你喜欢

转载自blog.csdn.net/qq_36688143/article/details/84984699
今日推荐