在C/C++语言中,sizeof它可以获取一个对象或者类型所占的内存字节数。在C/C++中需要sizeof是因为移植,不同的数据类型在不同的机器上大小可能不同,程序员必须知道对应的数据类型大小。
为什么Java中没有sizeof()?Java是一种纯面向对象的编程语言,它将内存管理的细节都交给Java Virtual Machine(JVM)进行管理。我们都说java是跨平台的,这里的平台确切说是jvm,java可以在任意版本的jvm上运行。但是jvm不是跨操作系统平台的,jvm主要用C语言编写的。操作系统种类是有限的,oracle公司在各种操系下都发布jvm。这样就间接的实现了java跨操作系统平台。
Windows 下Jvm有32位和64位两版本。8种基本数据类型占用的空间都是一样的,但new一个对象所占用的空间却很大差异。
对象 | 32位(byte) | 64位(byte) |
new Object() | 8 | 16 |
new Integer(1) | 16 | 16 |
new Short((short) 1) | 16 | 16 |
new Boolean(true) | 16 | 16 |
new Byte((byte) 0) | 16 | 16 |
new Character((char) 1) | 16 | 16 |
new Float(1) | 16 | 16 |
new Double(1) | 16 | 24 |
new Long(1) | 16 | 24 |
new String() | 16 | 24 |
new Object[0] | 16 | 16 |
new Object[1] | 16 | 24 |
new Object[2] | 24 | 24 |
new Object[3] | 24 | 32 |
new Object[4] | 32 | 32 |
new Object[5] | 32 | 40 |
表格中的数据获取方法:
public class TestSize {
public static void main(String args[]) throws Exception {
sizeOf();
}
private static Object types() {
return new Object();
// return new Integer(1);
// return new Short((short) 1);
// return new Long(1);
// return new Byte((byte) 0);
// return new Character((char) 1);
// return new Float(1);
// return new Double(1);
// return new Boolean(true);
// return new String();
// return new Object[0];
// return new Object[1];
// return new Object[2];
// return new Object[3];
// return new Object[4];
// return new Object[5];
}
private static final Runtime rTime = Runtime.getRuntime();
private static void sizeOf() {
final int count = 100000;
Object[] objs = new Object[count];
runGC();
long heapSizeBefore = usedMemory(); // before memory size
for (int i = 0; i < count; i++) {
objs[i] = types();
}
runGC();
long heapSizeAfter = usedMemory(); // after memory size
System.out.println("heapSizeBefore = " + heapSizeBefore);
System.out.println("heapSizeAfter = " + heapSizeAfter);
Long size = Math.round((heapSizeAfter - heapSizeBefore) / (double) count);
System.out.println(objs[0].getClass().getSimpleName() + "_size=" + size);
}
private static void runGC() {
long usedMemOld = 0;
long usedMemNew = usedMemory();
while (usedMemOld != usedMemNew) {//循环多次
rTime.runFinalization();//强制调用已经失去引用的对象的finalize方法
rTime.gc();//进行垃圾收集
Thread.yield();//使当前线程从执行状态(运行状态)变为可执行态(就绪状态),给jvm去执行gc
usedMemOld = usedMemNew;
usedMemNew = usedMemory();
}
}
private static long usedMemory() {
return rTime.totalMemory() - rTime.freeMemory();
}
}
1、观察上表,可以发现对象占用空间都是是8的倍数。
1.1、对象数组是存放对象地址的,在32位环境下,new Object[0]占16btye,new Object[1]占16btye,new Object[2]占24btye,new Object[3]占24btye,new Object[4]占32btye,new Object[5]占32byte。我们可以得出地址位占4btye。new Object[2]和new Object[3]为什么都占24byte,其实Object[2]只需要20btye就可以了,但是20不是8的倍数,所以又加了4byte。在64位环境下也是如此。
1.2、32操作系统最大支持4G内存,2^32*1byte=4G。64操作系统理论是2^64*1byte=17179869184G,实际中不可能用到这么大的内存,目前64位windows系统最大只支持192G,而当前最好主板只能加到64G,主流还是16G。有没有发现一个问题,32位和64位jvm中地址为都是占用4byte(32bit),32bit的寻址范围是0~4G,32位jvm是没有问题的,但是64位jvm的地址位按常理来推算应该是64bit=8byte,地址应该占8byte,为什么是4byte?这就涉及到jvm的指针压缩技术。当jvm是64位且内存小于32G会默认开启指针压缩技术。什么是指针压缩技术?简单的理解就是:jvm分配内存空间都是8byte的倍数,那么8byte就是jvm分配内存的最小单位,因此8byte只需要一个地址,2^32*8byte=32G,指针压缩技最大只能支持32G。
对象 | 空间 byte |
new Object[0] | 24 |
new Object[1] | 32 |
new Object[2] | 40 |
new Object[3] | 48 |
new Object[4] | 56 |
new Object[5] | 64 |