Java中一个对象的内存消耗是多less?

100个属性与100个对象相同的一个对象所占用的内存空间是多less?

多less内存分配给一个对象?
添加属性时使用了多less额外空间?

Mindprod指出,这不是一个简单的问题要回答:

一个JVM可以自由地存储任何内部数据,无论是大的还是小的endian,任何数量的填充或开销,尽pipe原语必须performance得好像他们有正式的大小。
例如,JVM或本地编译器可能决定像BitSet一样在64位长块中存储boolean[] 。 它不必告诉你,只要程序给出了相同的答案。

  • 它可能会在堆栈上分配一些临时对象。
  • 它可能会优化一些variables或方法调用完全不存在replace它们的常量。
  • 它可以版本化方法或循环,也就是编译一个方法的两个版本,每个版本都针对特定情况进行了优化,然后决定前面哪一个要调用。

那么硬件和操作系统当然有多层caching,片上caching,SRAMcaching,DRAMcaching,普通RAM工作集和磁盘上的后备存储。 您的数据可能会在每个caching级别重复。 所有这些复杂性意味着您只能非常粗略地预测内存消耗。

测量方法

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

要可视化实际的对象布局,足迹和引用,可以使用JOL(Java对象布局)工具 。

对象头和对象引用

在一个现代的64位JDK中,一个对象有一个12字节的头部,填充到8个字节的倍数,所以最小的对象大小是16个字节。 对于32位JVM,开销为8个字节,填充为4个字节的倍数。 (来自Dmitry Spikhalskiy的回答 , Jayen的回答和JavaWorld 。)

通常情况下,引用在32位平台或64位平台(高达-Xmx32G上是4个字节; 和32Gb以上的8个字节( -Xmx32G )。 (请参阅压缩的对象引用 。)

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

盒装types,数组和string

与原始types(来自JavaWorld )相比,装箱包装有开销:

  • Integer :16字节的结果比我预期的要差一点,因为一个int值可以适合4个额外的字节。 与我可以将该值存储为基本types相比,使用Integer花费我300%的内存开销

  • Long :也是16字节:显然,对于特定的CPUtypes,堆上实际的对象大小是由特定的JVM实现完成的低级内存alignment。 它看起来像一个Long是8个字节的Object开销,再加上8个字节的实际long值。 相比之下, Integer有一个未使用的4字节的漏洞,很可能是因为JVM使用8字节的字边界强制对象alignment。

其他容器也是昂贵的:

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

    在一个int[dim1][dim2]数组实例中,每个嵌套的int[dim2]数组本身都是一个Object 。 每个添加通常的16字节数组开销。 当我不需要一个三angular形或不规则的数组时,它代表纯开销。 arrays尺寸大不相同时,影响会增大。

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

  • StringString的内存增长跟踪其内部字符数组的增长。 但是, String类会增加另外24个字节的开销。

    对于大小为10个字符或更less的非空String ,相对于有效负载(每个字符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 } 

一个天真的总和会build议一个X的实例将使用17个字节。 但是,由于alignment(也称为填充),JVM以8个字节的倍数分配内存,所以不是17个字节,而是分配24个字节。

每个对象对于其关联的监视器和types信息以及字段本身都具有一定的开销。 除此之外,JVM看起来合适(我相信)的领域可以布置得相当多 – 但是如另一个答案所示 ,至less有些 JVM会打包得相当紧密。 考虑这样一个类:

 public class SingleByte { private byte b; } 

VS

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

在32位JVM上,我希望有100个SingleByte实例占用1200个字节(由于填充/alignment,8字节的开销+ 4字节的字段)。 我希望OneHundredBytes一个实例需要108个字节 – 开销,然后100个字节,打包。 它可以肯定会因JVM而异 – 一个实现可能决定不打包OneHundredBytes的字段,从而导致它占用408字节(= 8字节开销+ 4 * 100alignment/填充字节)。 在64位的JVM上,开销可能会更大(不确定)。

编辑:见下面的评论; 显然HotSpot SingleByte到8字节的边界而不是32,所以每个SingleByte实例将占用16个字节。

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

这取决于架构/ 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个对象相同的一个对象所占用的内存空间是多less?

没有。

多less内存分配给一个对象?

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

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

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

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

我从另一个答案中提到的java.lang.instrument.Instrumentation方法中得到了非常好的结果。 有关它的使用的好例子,请参阅JavaSpecialists的新闻简报中的Instrumentation Memory Counter和SourceForge上的java.sizeOf库。

一个程序的总使用/释放内存可以通过程序获得

 java.lang.Runtime.getRuntime(); 

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

 package test; import java.util.ArrayList; import java.util.List; 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 a object Person yourself... List < Person > list = new ArrayList < Person > (); for (int i = 0; i <= 100000; 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)); } } 

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

这取决于类variables,或者你可以调用java中的状态内存使用。

它也有一些额外的内存要求标题和引用。

Java对象使用的堆内存包括

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

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

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

Java中的对象还需要一些“pipe家”信息,例如logging一个对象的类,ID和状态标志,例如对象当前是否可到达,当前是同步locking等。

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

虽然这些是主要的内存消费者,但是jvm也需要额外的字段,有时候像代码的alignment等

原始types的大小

布尔&字节 – 1

char&short – 2

int&float – 4

长&双 – 8

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

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

在这里输入图像描述

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf也是非常丰富的,例如:;

在这里输入图像描述

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

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

有关使用多less内存的规则取决于JVM实现和CPU体系结构(例如,32位与64位)。

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

问候, 马库斯