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

一个拥有100个属性的对象所消耗的内存空间是否与100个对象各有一个属性所消耗的内存空间相同?

为一个对象分配了多少内存?< br > 添加属性时使用了多少额外空间?< / p >

141064 次浏览

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

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

每个对象对于其关联的监视器和类型信息以及字段本身都有一定的开销。除此之外,字段可以按照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字节。

无论哪种方式,“单个大对象”至少与多个小对象一样有效——对于像这样的简单情况。

Mindprod指出这不是一个简单的问题:

JVM可以在内部以任何它喜欢的方式存储数据,大或小的端序,有任何数量的填充或开销,尽管原语必须表现得像它们有正式的大小一样 例如,JVM或本机编译器可能决定将boolean[]存储在64位的长块中,如BitSet。它不需要告诉你,只要程序给出相同的答案
  • 它可能会在堆栈上分配一些临时对象。
  • 它可以优化一些完全不存在的变量或方法调用,将它们替换为常量。
  • 它可能会对方法或循环进行版本化,即编译一个方法的两个版本,每个版本都针对特定情况进行了优化,然后预先决定调用哪个版本。

当然硬件和操作系统有多层缓存,芯片缓存,SRAM缓存,DRAM缓存,普通RAM工作集和磁盘上的备份存储。您的数据可以在每个缓存级别复制。所有这些复杂性意味着您只能非常粗略地预测RAM消耗。

测量方法

你可以使用Instrumentation.getObjectSize()来获得一个对象所消耗的存储空间的估计值。

为了可视化实际对象的布局、占用空间和引用,可以使用JOL (Java对象布局)工具. xml对象

对象头文件和对象引用

在现代64位JDK中,对象具有12字节的报头,填充为8字节的倍数,因此最小对象大小为16字节。对于32位jvm,开销为8字节,填充为4字节的倍数。(从Dmitry Spikhalskiy的答案Jayen的答案,和JavaWorld。)

通常,引用在32位平台或64位平台上是4字节,直到-Xmx32G;32Gb以上8字节(-Xmx32G)。(参见压缩对象引用。)

因此,64位JVM通常需要30-50%以上的堆空间。(我应该使用32位还是64位JVM?, 2012, JDK 1.7)

盒装类型、数组和字符串

与基元类型相比,盒装包装器有开销(来自JavaWorld):

  • Integer: 16字节的结果比我预期的要差一些,因为int值只能容纳4个额外的字节。与将值存储为基本类型时相比,使用Integer要花费300%的内存开销

  • Long: 16 bytes also:显然,堆上的实际对象大小取决于特定CPU类型的特定JVM实现所做的低级内存对齐。看起来Long是8个字节的Object开销,加上8个字节的实际长值。相比之下,Integer有一个未使用的4字节洞,很可能是因为JVM我使用强制对象对齐在8字节的字边界上。

其他容器也很昂贵:

  • 多维数组:它提供了另一个惊喜 开发人员通常在数值和科学计算中使用int[dim1][dim2]这样的构造

    int[dim1][dim2]数组实例中,每个嵌套的int[dim2]数组本身就是一个Object。每个都增加了通常的16字节数组开销。当我不需要三角形或不规则数组时,这表示纯开销。当数组尺寸相差很大时,影响会增大。

    例如,int[128][2]实例占用3,600字节。与int[256]实例使用的1040字节相比(具有相同的容量),3600字节代表246%的开销。在byte[256][1]的极端情况下,开销因子几乎是19!与C/ c++相比,在C/ c++中,相同的语法不会增加任何存储开销。< / p >

  • String: String的内存增长跟踪其内部char数组的增长。然而,String类又增加了24字节的开销。

    对于10个字符或更少的非空String,相对于有效负载(每个字符2个字节加上长度4个字节),增加的开销范围为100%到400%。< / p >

对齐

考虑实例对象:

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
}

naïve求和表明X的实例将使用17个字节。然而,由于对齐(也称为填充),JVM以8字节的倍数分配内存,因此它将分配24字节而不是17字节。

我已经从另一个答案中提到的java.lang.instrument.Instrumentation方法得到了非常好的结果。有关它使用的好例子,请参阅条目,javaspecalists ' Newsletter中的仪表内存计数器 .和SourceForge上的java.sizeOf库。

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

关于SUN JVM的详细规则,请检查我的旧博客

< p >问候, 马库斯 < / p >

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

程序的总已用/空闲内存可以在程序中通过

java.lang.Runtime.getRuntime();

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

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

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

它取决于类变量,或者你可以在java中调用作为状态的内存使用。

对于头文件和引用,它还需要一些额外的内存。

Java对象使用的堆内存包括

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

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

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

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

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

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

基本类型的大小

__abc0——1

__abc0—2

__abc0—4

__abc0—8

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

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

enter image description here

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

enter image description here

这取决于架构/jdk。对于现代JDK和64位体系结构,一个对象有12字节的头部和8字节的填充-因此最小对象大小是16字节。你可以使用一个名为Java对象布局的工具来确定大小,并获得任何实体的对象布局和内部结构的详细信息,或者通过类引用来猜测这些信息。在我的环境中输出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:

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

一个拥有100个属性的对象所消耗的内存空间是否与100个对象各有一个属性所消耗的内存空间相同?

不。

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

  • 32位的开销是8字节,64位的是12字节;然后四舍五入为4字节(32位)或8字节(64位)的倍数。

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

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