Java中一个对象的内存消耗是多少?
100个属性与100个对象相同的一个对象是否消耗了内存空间,每个属性都有一个属性?
多少内存分配给一个对象?
添加属性时需要多少额外空间?
Mindprod指出,这不是一个直截了当的回答问题:
JVM可以自由地以任何方式存储数据,这些数据可以是内部的,大的或小的endian,任何数量的填充或开销,尽管基元必须表现得好像他们有官方的大小。
例如,JVM或本机编译器可能决定将一个boolean[]
存储为像BitSet
这样的64位长块。 它不必告诉你,只要程序给出相同的答案。
当然,硬件和操作系统具有多层高速缓存,片上高速缓存,SRAM高速缓存,DRAM高速缓存,普通RAM工作集以及磁盘上的后备存储。 您的数据可能会在每个缓存级别进行复制。 所有这些复杂性意味着您只能非常粗略地预测RAM消耗。
测量方法
您可以使用Instrumentation.getObjectSize()
来获取对象所消耗的存储空间的估计值。
要可视化实际的对象布局,占位面积和引用,可以使用JOL(Java对象布局)工具。
对象头和对象引用
在现代的64位JDK中,一个对象有一个12字节的头部,填充为8个字节的倍数,所以最小对象大小为16个字节。 对于32位JVM,开销为8个字节,填充为4个字节的倍数。 (来自Dmitry Spikhalskiy的回答,Jayen的回答和JavaWorld。)
通常,引用在32位平台或64位平台(高达-Xmx32G
上为4个字节; 和32Gb以上的8个字节( -Xmx32G
)。 (请参阅压缩的对象引用。)
因此,64位JVM通常需要30-50%的堆空间。 (我应该使用32位还是64位JVM?,2012,JDK 1.7)
盒装类型,数组和字符串
与原始类型(来自JavaWorld)相比,装箱包装有开销:
Integer
:16字节的结果比我预期的要差一点,因为一个int
值可以只适合4个额外的字节。 与我可以将该值存储为基本类型时相比,使用Integer
花费我的内存开销为300%
Long
:16字节:显然,对于特定的CPU类型,堆上的实际对象大小由特定的JVM实现完成的低级内存对齐。 它看起来像一个Long
是8个字节的Object开销,再加上8个字节的实际Long值。 相比之下, Integer
有一个未使用的4字节漏洞,很可能是因为JVM使用8字节字边界强制对象对齐。
其他容器也很昂贵:
多维数组 :它提供了另一个惊喜。
开发人员通常在数值计算和科学计算中使用像int[dim1][dim2]
这样的构造。
在一个int[dim1][dim2]
数组实例中,每个嵌套的int[dim2]
数组本身都是一个Object
。 每个添加通常的16字节数组开销。 当我不需要三角形或不齐整的数组时,表示纯粹的开销。 当阵列尺寸差异很大时,影响会增大。
例如, int[128][2]
实例需要3,600个字节。 与int[256]
实例使用的具有相同容量的1,040字节相比,3,600字节表示246%的开销。 在byte[256][1]
的极端情况下,开销因子几乎为19! 将其与C / C ++情况进行比较,其中相同的语法不会增加任何存储开销。
String
: String
的内存增长跟踪其内部字符数组的增长。 但是, String
类会增加24个字节的开销。
对于大小为10个字符或更小的非空String
,相对于有效负载(每个字符2个字节加上4个字节)的额外开销成本范围为100%到400%。
对准
考虑这个例子对象:
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
一个天真的总和会表明X
一个实例将使用17个字节。 但是,由于对齐(也称为填充),JVM以8个字节的倍数分配内存,所以不是17个字节,而是分配24个字节。
每个对象对其关联的监视器和类型信息以及字段本身都有一定的开销。 除此之外,字段可以布置得非常多,但是JVM看起来合适(我相信) - 但是如另一个答案所示,至少有些JVM会相当紧密地打包。 考虑这样一个类:
public class SingleByte
{
private byte b;
}
VS
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
在32位JVM上,我希望100个SingleByte
实例占用1200个字节(由于填充/对齐,8字节的开销+ 4字节的字段)。 我希望OneHundredBytes
一个实例需要108个字节 - 开销,然后是100个字节,打包。 它可以肯定会因JVM而异 - 一个实现可能决定不打包OneHundredBytes
的字段,导致它占用408个字节(= 8字节开销+ 4 * 100对齐/填充字节)。 在64位JVM上,开销可能会更大(不确定)。
编辑:请参阅下面的评论; 显然HotSpot SingleByte
到8字节边界而不是32,因此每个SingleByte
实例需要16个字节。
无论哪种方式,“单个大对象”至少与多个小对象一样高效 - 对于这样的简单情况。
这取决于架构/ jdk。 对于现代的JDK和64位体系结构对象,有12个字节的标题和填充8个字节 - 因此最小对象大小为16个字节。 您可以使用称为Java对象布局的工具来确定大小并获取有关对象布局和任何实体内部结构的详细信息,或者通过类参考来猜测此信息。 例如,在我的环境中输出Integer:
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
因此,对于Integer,实例大小为16个字节,因为4个字节的int紧跟在标题之后和填充边界之前。
代码示例:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
如果你使用maven来获取JOL:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
链接地址: http://www.djcxy.com/p/35049.html