简体   繁体   English

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

[英]What is the memory consumption of an object in Java?

Is the memory space consumed by one object with 100 attributes the same as that of 100 objects, with one attribute each?一个有100个属性的对象消耗的内存空间和100个对象每个有一个属性消耗的内存空间是一样的吗?

How much memory is allocated for an object?为一个对象分配了多少内存?
How much additional space is used when adding an attribute?添加属性时使用了多少额外空间?

Mindprod points out that this is not a straightforward question to answer: Mindprod指出,这不是一个可以直接回答的问题:

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. JVM 可以自由地在内部以任何它喜欢的方式存储数据,大端或小端,具有任意数量的填充或开销,但原语必须表现得好像它们具有官方大小一样。
For example, the JVM or native compiler might decide to store a boolean[] in 64-bit long chunks like a BitSet .例如,JVM 或本机编译器可能决定将boolean[]存储在 64 位长块中,如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, ie 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.当然,硬件和操作系统有多层缓存,片上缓存、SRAM 缓存、DRAM 缓存、普通 RAM 工作集和磁盘后备存储。 Your data may be duplicated at every cache level.您的数据可能会在每个缓存级别重复。 All this complexity means you can only very roughly predict RAM consumption.所有这些复杂性意味着您只能非常粗略地预测 RAM 消耗。

Measurement methods测量方法

You can use Instrumentation.getObjectSize() to obtain an estimate of the storage consumed by an object.您可以使用Instrumentation.getObjectSize()获取对象消耗的存储空间的估计值。

To visualize the actual object layout, footprint, and references, you can use the JOL (Java Object Layout) tool .要可视化实际的对象布局、封装和引用,您可以使用JOL(Java 对象布局)工具

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.在现代 64 位 JDK 中,对象具有 12 字节的标头,填充为 8 字节的倍数,因此最小对象大小为 16 字节。 For 32-bit JVMs, the overhead is 8 bytes, padded to a multiple of 4 bytes.对于 32 位 JVM,开销为 8 字节,填充为 4 字节的倍数。 (From Dmitry Spikhalskiy's answer , Jayen's answer , and JavaWorld .) (从梅德Spikhalskiy的回答Jayen的回答,和JavaWorld的。)

Typically, references are 4 bytes on 32bit platforms or on 64bit platforms up to -Xmx32G ;通常,引用在 32 位平台或 64 位平台上为 4 个字节,最高可达-Xmx32G and 8 bytes above 32Gb ( -Xmx32G ).和 8 字节以上 32Gb ( -Xmx32G )。 (See compressed object references .) (请参阅 压缩对象引用。)

As a result, a 64-bit JVM would typically require 30-50% more heap space.因此,64 位 JVM 通常需要多 30-50% 的堆空间。 ( Should I use a 32- or a 64-bit JVM? , 2012, JDK 1.7) 我应该使用 32 位还是 64 位 JVM? ,2012,JDK 1.7)

Boxed types, arrays, and strings盒装类型、数组和字符串

Boxed wrappers have overhead compared to primitive types (from JavaWorld ):与原始类型(来自JavaWorld )相比,盒装包装器具有开销:

  • Integer : The 16-byte result is a little worse than I expected because an int value can fit into just 4 extra bytes. Integer :16 字节的结果比我预期的要差一点,因为int值只能容纳 4 个额外的字节。 Using an Integer costs me a 300 percent memory overhead compared to when I can store the value as a primitive type与可以将值存储为原始类型相比,使用Integer花费了 300% 的内存开销

  • 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. Long :16 字节也:显然,堆上的实际对象大小受制于由特定 CPU 类型的特定 JVM 实现完成的低级内存对齐。 It looks like a Long is 8 bytes of Object overhead, plus 8 bytes more for the actual long value.看起来Long是 8 个字节的 Object 开销,加上实际 long 值的 8 个字节。 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.相比之下, Integer有一个未使用的 4 字节空洞,很可能是因为我使用的 JVM 强制在 8 字节字边界上对齐对象。

Other containers are costly too:其他容器也很昂贵:

  • Multidimensional arrays : it offers another surprise.多维数组:它提供了另一个惊喜。
    Developers commonly employ constructs like int[dim1][dim2] in numerical and scientific computing.开发人员通常在数值和科学计算中使用像int[dim1][dim2]

    In an int[dim1][dim2] array instance, every nested int[dim2] array is an Object in its own right.int[dim1][dim2]数组实例中,每个嵌套的int[dim2]数组本身就是一个Object Each adds the usual 16-byte array overhead.每个都会增加通常的 16 字节数组开销。 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.例如,一个int[128][2]实例占用 3,600 字节。 Compared to the 1,040 bytes an int[256] instance uses (which has the same capacity), 3,600 bytes represent a 246 percent overhead.int[256]实例使用的 1,040 字节(具有相同容量)相比,3,600 字节代表 246% 的开销。 In the extreme case of byte[256][1] , the overhead factor is almost 19!byte[256][1]的极端情况下,开销因子几乎是 19! Compare that to the C/C++ situation in which the same syntax does not add any storage overhead.将其与相同语法不增加任何存储开销的 C/C++ 情况进行比较。

  • String : a String 's memory growth tracks its internal char array's growth. StringString的内存增长跟踪其内部字符数组的增长。 However, the String class adds another 24 bytes of overhead.但是, String类又增加了 24 个字节的开销。

    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.对于大小为 10 个字符或更少的非空String ,相对于有用负载(每个字符 2 个字节加上长度为 4 个字节)的额外开销成本在 100% 到 400% 之间。

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

It depends on architecture/jdk.这取决于架构/jdk。 For a modern JDK and 64bit architecture, an object has 12-bytes header and padding by 8 bytes - so minimum object size is 16 bytes.对于现代 JDK 和 64 位体系结构,对象具有 12 字节的标头和 8 字节的填充 - 因此最小对象大小为 16 字节。 You can use a tool called Java Object Layout to determine a size and get details about object layout and internal structure of any entity or guess this information by class reference.您可以使用称为Java 对象布局的工具来确定大小并获取有关任何实体的对象布局和内部结构的详细信息,或者通过类引用来猜测此信息。 Example of an output for Integer on my environment:我的环境中 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

So, for Integer, instance size is 16 bytes, because 4-bytes int compacted in place right after header and before padding boundary.因此,对于 Integer,实例大小为 16 字节,因为 4 字节 int 紧接在标头之后和填充边界之前压缩到位。

Code sample:代码示例:

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());
}

If you use maven, to get JOL:如果您使用 maven,要获得 JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>

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.除此之外,几乎可以按照 JVM 认为合适的方式布置字段(我相信) - 但如另一个答案所示,至少一些JVM 会打包得相当紧。 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).在 32 位 JVM 上,我希望SingleByte 100 个实例占用 1200 个字节(由于填充/对齐,8 个字节的开销 + 4 个字段用于该字段)。 I'd expect one instance of OneHundredBytes to take 108 bytes - the overhead, and then 100 bytes, packed.我希望OneHundredBytes一个实例占用 108 个字节 - 开销,然后是 100 个字节,打包。 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).不过它肯定会因 JVM 而异——一种实现可能决定不将字段打包在OneHundredBytes ,导致它占用 408 字节(= 8 字节开销 + 4 * 100 对齐/填充字节)。 On a 64 bit JVM the overhead may well be bigger too (not sure).在 64 位 JVM 上,开销也可能更大(不确定)。

EDIT: See the comment below;编辑:请参阅下面的评论; apparently HotSpot pads to 8 byte boundaries instead of 32, so each instance of SingleByte would take 16 bytes.显然 HotSpot SingleByte到 8 个字节的边界而不是 32 个,因此SingleByte每个实例将占用 16 个字节。

Either way, the "single large object" will be at least as efficient as multiple small objects - for simple cases like this.无论哪种方式,“单个大对象”至少与多个小对象一样有效 - 对于像这样的简单情况。

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));
     }
 }

It appears that every object has an overhead of 16 bytes on 32-bit systems (and 24-byte on 64-bit systems).似乎每个对象在 32 位系统上都有 16 字节的开销(在 64 位系统上是 24 字节)。

http://algs4.cs.princeton.edu/14analysis/ is a good source of information. http://algs4.cs.princeton.edu/14analysis/是一个很好的信息来源。 One example among many good ones is the following.以下是众多优秀示例中的一个示例。

在此处输入图片说明

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

在此处输入图片说明

Is the memory space consumed by one object with 100 attributes the same as that of 100 objects, with one attribute each?一个有100个属性的对象消耗的内存空间和100个对象每个有一个属性消耗的内存空间是一样的吗?

No.不。

How much memory is allocated for an object?为一个对象分配了多少内存?

  • The overhead is 8 bytes on 32-bit, 12 bytes on 64-bit;开销是32位8字节,64位12字节; and then rounded up to a multiple of 4 bytes (32-bit) or 8 bytes (64-bit).然后四舍五入为 4 字节(32 位)或 8 字节(64 位)的倍数。

How much additional space is used when adding an attribute?添加属性时使用了多少额外空间?

  • Attributes range from 1 byte (byte) to 8 bytes (long/double), but references are either 4 bytes or 8 bytes depending not on whether it's 32bit or 64bit, but rather whether -Xmx is < 32Gb or >= 32Gb: typical 64-bit JVM's have an optimisation called "-UseCompressedOops" which compress references to 4 bytes if the heap is below 32Gb.属性的范围从1个字节(字节)到8个字节(长/双),但引用或者取决于-Xmx无论它是32位或64位,而不论是<32Gb的或> = 32Gb的4个字节或8个字节:典型64 -bit JVM 有一个称为“-UseCompressedOops”的优化,如果堆低于 32Gb,它会将引用压缩到 4 个字节。

The question will be a very broad one.这个问题将是一个非常广泛的问题。

It depends on the class variable or you may call as states memory usage in java.这取决于类变量,或者您可以在 java 中调用作为状态内存使用情况。

It also has some additional memory requirement for headers and referencing.它还对标头和引用有一些额外的内存要求。

The heap memory used by a Java object includes Java 对象使用的堆内存包括

  • memory for primitive fields, according to their size (see below for Sizes of primitive types);原始字段的内存,根据它们的大小(参见下面的原始类型的大小);

  • memory for reference fields (4 bytes each);用于参考字段的内存(每个 4 个字节);

  • an object header, consisting of a few bytes of "housekeeping" information;一个对象头,由几个字节的“内务处理”信息组成;

Objects in java also requires some "housekeeping" information, such as recording an object's class, ID and status flags such as whether the object is currently reachable, currently synchronization-locked etc. Java 中的对象还需要一些“内务处理”信息,例如记录对象的类、ID 和状态标志,例如对象当前是否可达、当前是否同步锁定等。

Java object header size varies on 32 and 64 bit jvm. Java 对象标头大小在 32 位和 64 位 jvm 上有所不同。

Although these are the main memory consumers jvm also requires additional fields sometimes like for alignment of the code etc虽然这些是主要的内存消费者,但 jvm 有时也需要额外的字段,例如代码对齐等

Sizes of primitive types原始类型的大小

boolean & byte -- 1布尔值和字节-- 1

char & short -- 2字符和短- 2

int & float -- 4整数和浮点数-- 4

long & double -- 8长双倍-- 8

No, registering an object takes a bit of memory too.不,注册一个对象也需要一些内存。 100 objects with 1 attribute will take up more memory.具有 1 个属性的 100 个对象将占用更多内存。

I've gotten very good results from the java.lang.instrument.Instrumentation approach mentioned in another answer.我从另一个答案中提到的java.lang.instrument.Instrumentation方法中获得了非常好的结果。 For good examples of its use, see the entry, Instrumentation Memory Counter from the JavaSpecialists' Newsletter and the java.sizeOf library on SourceForge.有关其使用的良好示例,请参阅 JavaSpecialists' Newsletter 中的条目Instrumentation Memory Counter和 SourceForge 上的java.sizeOf库。

In case it's useful to anyone, you can download from my web site a small Java agent for querying the memory usage of an object .如果它对任何人有用,您可以从我的网站下载一个用于查询对象内存使用情况的小型Java 代理 It'll let you query "deep" memory usage as well.它还可以让您查询“深度”内存使用情况。

不,100 个小物体比一个大物体需要更多的信息(内存)。

The rules about how much memory is consumed depend on the JVM implementation and the CPU architecture (32 bit versus 64 bit for example).有关消耗多少内存的规则取决于 JVM 实现和 CPU 架构(例如 32 位与 64 位)。

For the detailed rules for the SUN JVM check my old blog SUN JVM的详细规则查看我的旧博客

Regards, Markus问候,马库斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM