235

Is the memory space consumed by one object with 100 attributes the same as that of 100 objects, with one attribute each?

How much memory is allocated for an object?
How much additional space is used when adding an attribute?

4

12 回答 12

194

Mindprod points out that this is not a straightforward question to answer:

A JVM is free to store data any way it pleases internally, big or little endian, with any amount of padding or overhead, though primitives must behave as if they had the official sizes.
For example, the JVM or native compiler might decide to store a boolean[] in 64-bit long chunks like a BitSet. It does not have to tell you, so long as the program gives the same answers.

  • It might allocate some temporary Objects on the stack.
  • It may optimize some variables or method calls totally out of existence replacing them with constants.
  • It might version methods or loops, i.e. compile two versions of a method, each optimized for a certain situation, then decide up front which one to call.

Then of course the hardware and OS have multilayer caches, on chip-cache, SRAM cache, DRAM cache, ordinary RAM working set and backing store on disk. Your data may be duplicated at every cache level. All this complexity means you can only very roughly predict RAM consumption.

Measurement methods

You can use Instrumentation.getObjectSize() to obtain an estimate of the storage consumed by an object.

To visualize the actual object layout, footprint, and references, you can use the JOL (Java Object Layout) tool.

Object headers and Object references

In a modern 64-bit JDK, an object has a 12-byte header, padded to a multiple of 8 bytes, so the minimum object size is 16 bytes. For 32-bit JVMs, the overhead is 8 bytes, padded to a multiple of 4 bytes. (From Dmitry Spikhalskiy's answer, Jayen's answer, and JavaWorld.)

Typically, references are 4 bytes on 32bit platforms or on 64bit platforms up to -Xmx32G; and 8 bytes above 32Gb (-Xmx32G). (See compressed object references.)

As a result, a 64-bit JVM would typically require 30-50% more heap space. (Should I use a 32- or a 64-bit JVM?, 2012, JDK 1.7)

Boxed types, arrays, and strings

Boxed wrappers have overhead compared to primitive types (from JavaWorld):

  • Integer: The 16-byte result is a little worse than I expected because an int value can fit into just 4 extra bytes. Using an Integer costs me a 300 percent memory overhead compared to when I can store the value as a primitive type

  • Long: 16 bytes also: Clearly, actual object size on the heap is subject to low-level memory alignment done by a particular JVM implementation for a particular CPU type. It looks like a Long is 8 bytes of Object overhead, plus 8 bytes more for the actual long value. In contrast, Integer had an unused 4-byte hole, most likely because the JVM I use forces object alignment on an 8-byte word boundary.

Other containers are costly too:

  • Multidimensional arrays: it offers another surprise.
    Developers commonly employ constructs like int[dim1][dim2] in numerical and scientific computing.

    In an int[dim1][dim2] array instance, every nested int[dim2] array is an Object in its own right. Each adds the usual 16-byte array overhead. When I don't need a triangular or ragged array, that represents pure overhead. The impact grows when array dimensions greatly differ.

    For example, a int[128][2] instance takes 3,600 bytes. Compared to the 1,040 bytes an int[256] instance uses (which has the same capacity), 3,600 bytes represent a 246 percent overhead. In the extreme case of byte[256][1], the overhead factor is almost 19! Compare that to the C/C++ situation in which the same syntax does not add any storage overhead.

  • String: a String's memory growth tracks its internal char array's growth. However, the String class adds another 24 bytes of overhead.

    For a nonempty String of size 10 characters or less, the added overhead cost relative to useful payload (2 bytes for each char plus 4 bytes for the length), ranges from 100 to 400 percent.

Alignment

Consider this example object:

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
}

A naïve sum would suggest that an instance of X would use 17 bytes. However, due to alignment (also called padding), the JVM allocates the memory in multiples of 8 bytes, so instead of 17 bytes it would allocate 24 bytes.

于 2008-11-03T08:42:19.403 回答
37

这取决于架构/ 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 紧挨着 header 之后和填充边界之前压缩到位。

代码示例:

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>
于 2015-08-26T10:43:56.143 回答
29

Each object has a certain overhead for its associated monitor and type information, as well as the fields themselves. Beyond that, fields can be laid out pretty much however the JVM sees fit (I believe) - but as shown in another answer, at least some JVMs will pack fairly tightly. Consider a class like this:

public class SingleByte
{
    private byte b;
}

vs

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

On a 32-bit JVM, I'd expect 100 instances of SingleByte to take 1200 bytes (8 bytes of overhead + 4 bytes for the field due to padding/alignment). I'd expect one instance of OneHundredBytes to take 108 bytes - the overhead, and then 100 bytes, packed. It can certainly vary by JVM though - one implementation may decide not to pack the fields in OneHundredBytes, leading to it taking 408 bytes (= 8 bytes overhead + 4 * 100 aligned/padded bytes). On a 64 bit JVM the overhead may well be bigger too (not sure).

EDIT: See the comment below; apparently HotSpot pads to 8 byte boundaries instead of 32, so each instance of SingleByte would take 16 bytes.

Either way, the "single large object" will be at least as efficient as multiple small objects - for simple cases like this.

于 2008-11-03T08:39:52.233 回答
8

The total used / free memory of a program can be obtained in the program via

java.lang.Runtime.getRuntime();

The runtime has several methods which relate to the memory. The following coding example demonstrates its usage.

 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));
     }
 }
于 2013-01-24T12:55:32.727 回答
8

似乎每个对象在 32 位系统上都有 16 字节的开销(在 64 位系统上是 24 字节)。

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

在此处输入图像描述

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf也很丰富,例如:

在此处输入图像描述

于 2015-05-05T21:49:26.850 回答
7

1个100个属性的对象占用的内存空间和100个1个属性的对象占用的内存空间是一样的吗?

不。

为一个对象分配了多少内存?

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

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

  • 属性范围从 1 字节 (byte) 到 8 字节 (long/double),但引用是 4 字节或 8 字节,这取决于是 32 位还是 64 位,而取决于 -Xmx 是 < 32Gb 还是 >= 32Gb:典型 64 -bit JVM 有一个称为“-UseCompressedOops”的优化,如果堆小于 32Gb,它会将引用压缩到 4 个字节。
于 2016-02-15T11:16:15.507 回答
5

No, registering an object takes a bit of memory too. 100 objects with 1 attribute will take up more memory.

于 2008-11-03T08:26:17.720 回答
5

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

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

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

Java 对象使用的堆内存包括

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

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

  • 一个对象头,由几个字节的“管家”信息组成;

Java 中的对象还需要一些“管家”信息,例如记录对象的类、ID 和状态标志,例如对象当前是否可访问、当前是否已同步锁定等。

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

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

原始类型的大小

布尔值 & 字节-- 1

字符 & 短-- 2

整数和浮点数——4

长 & 双-- 8

于 2013-05-18T14:23:18.003 回答
3

我从另一个答案中提到的java.lang.instrument.Instrumentation方法获得了非常好的结果。有关其使用的良好示例,请参见JavaSpecialists' Newsletter 中的Instrumentation Memory Counter条目和 SourceForge 上的java.sizeOf库。

于 2008-11-04T01:27:30.220 回答
3

如果它对任何人有用,您可以从我的网站下载一个小型Java 代理,用于查询对象的内存使用情况。它还可以让您查询“深度”内存使用情况。

于 2009-04-07T17:10:51.767 回答
1

no, 100 small objects needs more information (memory) than one big.

于 2008-11-03T08:27:12.697 回答
0

关于消耗多少内存的规则取决于 JVM 实现和 CPU 架构(例如 32 位与 64 位)。

有关 SUN JVM 的详细规则,请查看我的旧博客

问候, 马库斯

于 2008-11-13T08:18:12.680 回答