一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?
为对象分配了多少内存?
添加属性时会使用多少额外空间?
Mindprod指出,这不是一个直截了当的问题:
JVM可以随意存储数据,无论是内部,大小还是小端,都有任何填充或开销,但原语必须表现得像官方大小一样.
例如,JVM或本机编译器可能决定存储一个boolean[]
64位长的块,如aBitSet
.它不必告诉你,只要程序给出相同的答案.
它可能会在堆栈上分配一些临时对象.
它可以优化一些完全不存在的变量或方法调用,用常量替换它们.
它可能是版本方法或循环,即编译方法的两个版本,每个版本针对特定情况进行优化,然后在前面决定调用哪一个.
当然,硬件和操作系统有多层缓存,片上缓存,SRAM缓存,DRAM缓存,普通RAM工作集和磁盘上的后备存储.您的数据可能会在每个缓存级别重复.所有这些复杂性意味着您只能非常粗略地预测RAM消耗.
您可以使用 Instrumentation.getObjectSize()
来获取对象所消耗的存储空间的估计值.
要可视化实际的对象布局,覆盖区和引用,可以使用JOL(Java对象布局)工具.
在现代的64位JDK中,对象具有12字节的头,填充为8字节的倍数,因此最小对象大小为16字节.对于32位JVM,开销为8个字节,填充为4个字节的倍数. (来自Dmitry Spikhalskiy的回答,Jayen的回答,以及JavaWorld.)
通常,引用在32位平台上或在64位平台上最多为4个字节-Xmx32G
; 和32Gb(-Xmx32G
)之上的8个字节. (请参阅压缩对象参考.)
因此,64位JVM通常需要30-50%的堆空间.(我应该使用32位还是64位JVM?,2012,JDK 1.7)
与原始类型(来自JavaWorld)相比,盒装包装器具有开销:
Integer
:16字节的结果比我预期的要差一点,因为一个int
值只能容纳4个额外的字节.Integer
与我将值存储为基本类型时相比,使用成本会产生300%的内存开销
Long
:16字节:显然,堆上的实际对象大小受特定CPU类型的特定JVM实现完成的低级内存对齐.它看起来像是一个Long
8字节的对象开销,再加上8字节的实际长值.相比之下,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
内存增长跟踪其内部char数组的增长.然而String
该类增加了另外24个字节的开销.对于
String
大小为10个字符或更小的非空,相对于有用有效负载的额外开销成本(每个字符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个字节的倍数分配内存,因此它将分配24个字节而不是17个字节.
这取决于架构/ jdk.对于现代JDK和64位体系结构,对象具有12字节头和8字节填充 - 因此最小对象大小为16字节.您可以使用名为Java Object Layout的工具来确定大小并获取有关任何实体的对象布局和内部结构的详细信息,或者通过类引用猜测此信息.我环境中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:
org.openjdk.jol jol-core 0.3.2
每个对象对其关联的监视器和类型信息以及字段本身都有一定的开销.除此之外,字段可以很好地布局,但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填充到8字节边界而不是32,所以每个实例SingleByte
将占用16个字节.
无论哪种方式,"单个大对象"将至少与多个小对象一样有效 - 对于像这样的简单情况.
不,注册一个对象也需要一些内存.具有1个属性的100个对象将占用更多内存.
看起来每个对象在32位系统上的开销为16字节(在64位系统上为24字节).
http://algs4.cs.princeton.edu/14analysis/是一个很好的信息来源.许多好的例子之一是以下.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf也非常有用,例如:
一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?
没有.
为对象分配了多少内存?
开销在32位上为8字节,在64位上为12字节; 然后向上舍入为4个字节(32位)或8个字节(64位)的倍数.
添加属性时会使用多少额外空间?
属性的范围从1个字节(字符/布尔值)为8个字节(长/双),但是参考文献是根据任一4个字节或8个字节不它是否是32位或64位,但-Xmx而是否是<32Gb的或> = 32Gb的:典型的64位JVM有一个名为"-UseCompressedOops"的优化,如果堆低于32Gb,它会将引用压缩为4个字节.