最近在做一个小项目,需要把所有数据刷到内存中,避免采用redis或者其他内存数据库需要多次IO,数据大约9万条,因此要估计下大概需要耗费多少内存,以评估可以在server上部署多少个应用,突然有点懵,期初都是通过Runtime.totalMemory-Runtime.freeMemory来计算,但是这样似乎并不是很好,下面我们来分析分析一个对象到底占用多少内存。
先看一个表:
[table]
|类型 |未压缩(字节)|压缩(字节)
|boolean | 1|1|
|byte | 1|1|
|short | 2|2|
|char | 2|2|
|int | 4|4|
|float | 4|4|
|long | 8|8|
|double |8|8|
| reference |8|4|
|数组| 24|16|
[/table]
以上数据受到jvm参数压缩未压缩(UseCompressedOops)设定影响,这是基础。

我们再来看看Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),对于padding,是因为占用字节数必须是8的倍数,你可以简单认为是凑数,因为因此内存占用也要从这几方面考虑。比如我们有一个类

class A{
    int a;
}



头对象未压缩是16字节,压缩是12字节,因此:


未压缩:16(头对象)+4(a)+4(padding)=24字节


压缩:12(头对象)+4(a)=16字节


再看如下代码


class A{
    int a;
    Integer b;
}




因为 b是对象的引用,需要占用8个字节,因此


未压缩:16(头对象)+4(a)+8(b)+4(padding)=32字节


压缩:12(头对象)+4(a)+8(b)=24字节



我们再分析下数组,数组是个特殊的对象,64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。


比如 Integer[0],长度为0,只有头对象,因此是24或者16


再比如Integer[3]


未压缩:24+8*3=28


压缩:16+4*3+4(padding)=32,你也应该知道Integer[4]在压缩下还是32 :D


最后再看一段代码:



static class B {
        int a;
        int b;
    }
static class C {
        int ba;
        B[] as = new B[3];

        C() {
            for (int i = 0; i < as.length; i++) {
                as[i] = new B();
            }
        }
    }




我们先来看下这个对象的内存结构吧:



[img]http://dl2.iteye.com/upload/attachment/0101/0632/5ed0fbcb-8b45-3ead-8bdd-7419394439e8.png[/img]



未压缩:


3个B实例:(16(头对象)+4(a)+4(b))*3=72


数组对象:24(头对象)+8(B引用)*3=48


C实例:16(头对象)+4(ba)+8(as引用)+4(padding)=32


压缩:


3个B实例:(12(头对象)+4(a)+4(b)+4(padding))*3=72


数组对象:16(头对象)+4(B引用)*3+4(padding)=32


C实例:12(头对象)+4(ba)+4(as引用)+4(padding)=24



因此一个c实例占用空间为:


未压缩:72+48+32=152


压缩:72+32+24=128



这就是对象占用空间的分析过程。



当然了,现实中,我们不可能去一个一个对象分析内存占用情况,那么代码如何实现呢