当前位置:  开发笔记 > 编程语言 > 正文

Java中对象的内存消耗是多少?

如何解决《Java中对象的内存消耗是多少?》经验,为你挑选了6个好方法。

一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?

为对象分配了多少内存?
添加属性时会使用多少额外空间?



1> VonC..:

Mindprod指出,这不是一个直截了当的问题:

JVM可以随意存储数据,无论是内部,大小还是小端,都有任何填充或开销,但原语必须表现得像官方大小一样.
例如,JVM或本机编译器可能决定存储一个boolean[]64位长的块,如a BitSet.它不必告诉你,只要程序给出相同的答案.

它可能会在堆栈上分配一些临时对象.

它可以优化一些完全不存在的变量或方法调用,用常量替换它们.

它可能是版本方法或循环,即编译方法的两个版本,每个版本针对特定情况进行优化,然后在前面决定调用哪一个.

当然,硬件和操作系统有多层缓存,片上缓存,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实现完成的低级内存对齐.它看起来像是一个Long8字节的对象开销,再加上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个字节.


@AlexWien:一些垃圾收集方案可能会施加与填充分开的最小对象大小.在垃圾收集期间,一旦将对象从旧位置复制到新位置,旧位置可能不再需要保存对象的数据,但是它需要保持对新位置的引用; 它还可能需要存储对发现第一个引用的对象的旧位置的引用以及该引用在旧对象中的偏移[因为旧对象可能仍包含尚未处理的引用].
64位JVM中的开销是16字节.
@AlexWien: Using memory at an object's old location to hold the garbage-collector's book-keeping information avoids the need to allocate other memory for that purpose, but may impose a minimum object size which is larger than would otherwise be required. I think at least one version of .NET garbage collector uses that approach; it would certainly be possible for some Java garbage collectors to do so as well.

2> Dmitry Spikh..:

这取决于架构/ 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



3> Jon Skeet..:

每个对象对其关联的监视器和类型信息以及字段本身都有一定的开销.除此之外,字段可以很好地布局,但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个字节.

无论哪种方式,"单个大对象"将至少与多个小对象一样有效 - 对于像这样的简单情况.


实际上,SingleByte的一个实例将在Sun JVM上占用16个字节,即8字节开销,4字节用于字段,然后4个字节用于对象填充,因为HotSpot编译器将所有内容舍入为8的倍数.

4> Mendelt..:

不,注册一个对象也需要一些内存.具有1个属性的100个对象将占用更多内存.



5> Arun..:

看起来每个对象在32位系统上的开销为16字节(在64位系统上为24字节).

http://algs4.cs.princeton.edu/14analysis/是一个很好的信息来源.许多好的例子之一是以下.

在此输入图像描述

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf也非常有用,例如:

在此输入图像描述



6> Jayen..:

一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?

没有.

为对象分配了多少内存?

开销在32位上为8字节,在64位上为12字节; 然后向上舍入为4个字节(32位)或8个字节(64位)的倍数.

添加属性时会使用多少额外空间?

属性的范围从1个字节(字符/布尔值)为8个字节(长/双),但是参考文献是根据任一4个字节或8个字节它是否是32位或64位,但-Xmx而是否是<32Gb的或> = 32Gb的:典型的64位JVM有一个名为"-UseCompressedOops"的优化,如果堆低于32Gb,它会将引用压缩为4个字节.

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有