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

208 java memory footprint

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

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

Von*_*onC 171

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
}
Run Code Online (Sandbox Code Playgroud)

一个简单的总和表明一个实例X将使用17个字节.但是,由于对齐(也称为填充),JVM以8个字节的倍数分配内存,因此它将分配24个字节而不是17个字节.

  • @AlexWien:一些垃圾收集方案可能会施加与填充分开的最小对象大小.在垃圾收集期间,一旦将对象从旧位置复制到新位置,旧位置可能不再需要保存对象的数据,但是它需要保持对新位置的引用; 它还可能需要存储对发现第一个引用的对象的旧位置的引用以及该引用在旧对象中的偏移[因为旧对象可能仍包含尚未处理的引用]. (3认同)
  • 64位JVM中的开销是16字节. (2认同)
  • @AlexWien:在对象的旧位置使用内存来保存垃圾收集器的簿记信息可避免为此目的分配其他内存的需要,但可能会施加比其他情况下所需的最小对象大小。我认为至少有一个版本的 .NET 垃圾收集器使用了这种方法;某些 Java 垃圾收集器当然也可以这样做。 (2认同)

Dmi*_*kiy 33

这取决于架构/ 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
Run Code Online (Sandbox Code Playgroud)

因此,对于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());
}
Run Code Online (Sandbox Code Playgroud)

如果你使用maven,要获得JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 28

每个对象对其关联的监视器和类型信息以及字段本身都有一定的开销.除此之外,字段可以很好地布局,但JVM认为合适(我相信) - 但正如另一个答案所示,至少有一些 JVM会打包得相当紧密.考虑这样一个类:

public class SingleByte
{
    private byte b;
}
Run Code Online (Sandbox Code Playgroud)

VS

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}
Run Code Online (Sandbox Code Playgroud)

在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的倍数. (9认同)

naz*_*art 7

程序的总已用/空闲内存可以通过以下方式在程序中获得

java.lang.Runtime.getRuntime();
Run Code Online (Sandbox Code Playgroud)

运行时有几个与内存相关的方法。下面的编码示例演示了它的用法。

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create an object Person yourself...
         List <Person> list = new ArrayList <Person> ();
         for (int i = 0; i <= 100_000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }

         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();

         // Run the garbage collector
         runtime.gc();

         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }
Run Code Online (Sandbox Code Playgroud)


Men*_*elt 5

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


Nik*_*wal 5

这个问题将是一个非常广泛的问题。

这取决于类变量,或者您可以在 java 中调用作为状态内存使用情况。

它还对标头和引用有一些额外的内存要求。

Java 对象使用的堆内存包括

  • 原始字段的内存,根据它们的大小(参见下面的原始类型的大小);

  • 用于参考字段的内存(每个 4 个字节);

  • 一个对象头,由几个字节的“内务处理”信息组成;

Java 中的对象还需要一些“内务处理”信息,例如记录对象的类、ID 和状态标志,例如对象当前是否可达、当前是否同步锁定等。

Java 对象标头大小在 32 位和 64 位 jvm 上有所不同。

虽然这些是主要的内存消费者,但 jvm 有时也需要额外的字段,例如代码对齐等

原始类型的大小

布尔值和字节-- 1

字符和短- 2

整数和浮点数-- 4

长双倍-- 8


Aru*_*run 5

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

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

在此输入图像描述

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

在此输入图像描述


Jay*_*yen 5

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

没有.

为对象分配了多少内存?

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

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

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

  • 字符是 16 位,而不是 8 位。 (2认同)