一个拥有100个属性的对象所消耗的内存空间是否与100个对象各有一个属性所消耗的内存空间相同?
为一个对象分配了多少内存? 添加属性时使用了多少额外空间?
一个拥有100个属性的对象所消耗的内存空间是否与100个对象各有一个属性所消耗的内存空间相同?
为一个对象分配了多少内存? 添加属性时使用了多少额外空间?
每个对象对于其关联的监视器和类型信息以及字段本身都有一定的开销。除此之外,字段可以按照JVM认为合适的方式进行布局(我相信)——但正如另一个答案所示,至少有些JVM会相当紧密地打包。考虑这样一个类:
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).
编辑:见下面的评论;显然HotSpot的边界是8字节,而不是32字节,因此每个SingleByte实例将占用16字节。
无论哪种方式,“单个大对象”至少与多个小对象一样有效——对于像这样的简单情况。
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. 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.
测量方法
您可以使用Instrumentation.getObjectSize()来获得对象消耗的存储空间的估估值。
要可视化实际的对象布局、占用空间和引用,可以使用JOL (Java对象布局)工具。
对象头文件和对象引用
在现代64位JDK中,对象具有12字节的报头,填充为8字节的倍数,因此最小对象大小为16字节。对于32位jvm,开销为8字节,填充为4字节的倍数。(来自Dmitry Spikhalskiy的回答、Jayen的回答和JavaWorld。)
通常,引用在32位平台上为4字节,在64位平台上为-Xmx32G;32Gb以上8字节(-Xmx32G)。(参见压缩对象引用。)
因此,64位JVM通常需要30-50%以上的堆空间。我应该使用32位的JVM还是64位的JVM?, 2012, JDK 1.7)
盒装类型、数组和字符串
与原始类型相比,盒装包装器的开销更大(来自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.
其他容器也很昂贵:
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.
对齐
考虑这个例子对象:
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方法中获得了非常好的结果。有关它的使用示例,请参阅java专家通讯和java。SourceForge上的sizeOf库。
程序的总已用/空闲内存可以在程序中通过
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有时也需要额外的字段,如代码的对齐等。
基本类型的大小
布尔和字节——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的信息也非常丰富,例如:
这取决于架构/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个对象各有一个属性所消耗的内存空间相同?
No.
为一个对象分配了多少内存?
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字节。