一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?
为对象分配了多少内存? 添加属性时会使用多少额外空间?
答案 0 :(得分:168)
Mindprod指出这不是一个直截了当的问题:
JVM可以随意存储数据,无论是内部,大小还是小端,都有任何填充或开销,但原语必须表现得像官方大小一样。
例如,JVM或本机编译器可能决定将boolean[]
存储为64位长块,如BitSet
。只要程序给出相同的答案,它就不必告诉你。
- 它可能会在堆栈上分配一些临时对象。
- 它可以优化一些完全不存在的变量或方法调用,用常量替换它们。
- 它可能是版本方法或循环,即编译方法的两个版本,每个版本针对特定情况进行优化,然后预先决定调用哪一个。
当然硬件和操作系统有多层缓存,片上缓存,SRAM缓存,DRAM缓存,普通RAM工作集和磁盘上的后备存储。您的数据可能会在每个缓存级别重复。所有这些复杂性意味着您只能非常粗略地预测RAM消耗。
您可以使用Instrumentation.getObjectSize()
来估算对象所消耗的存储空间。
要显示实际对象布局,足迹和引用,您可以使用JOL (Java Object Layout) tool。
在现代64位JDK中,一个对象有一个12字节的头,填充为8个字节的倍数,因此最小对象大小为16个字节。对于32位JVM,开销为8个字节,填充为4个字节的倍数。 (来自Dmitry Spikhalskiy's answer,Jayen's answer和JavaWorld。)
通常,引用在32位平台上或在64位平台上最多为-Xmx32G
为4个字节; 32Gb以上8字节(-Xmx32G
)。 (请参阅compressed object references。)
因此,64位JVM通常需要30-50%的堆空间。 (Should I use a 32- or a 64-bit JVM?,2012,JDK 1.7)
与基本类型(来自JavaWorld)相比,盒装包装器有开销:
Integer
:16字节的结果比我预期的要差一些,因为int
值只能容纳4个额外的字节。与我将值存储为基本类型时相比,使用Integer
花费了300%的内存开销
Long
:16字节:显然,堆上的实际对象大小受特定CPU类型的特定JVM实现完成的低级内存对齐的影响。看起来Long
是8字节的对象开销,加上8字节的实际长值。相比之下,Integer
有一个未使用的4字节漏洞,很可能是因为我使用的JVM强制对象在8字节字边界上对齐。
其他容器也很昂贵:
多维数组:它提供了另一个惊喜 开发人员通常在数值和科学计算中使用
int[dim1][dim2]
等结构。在
int[dim1][dim2]
数组实例中,每个嵌套的int[dim2]
数组本身都是Object
。每个都添加了通常的16字节数组开销。当我不需要三角形或粗糙的数组时,这表示纯粹的开销。当阵列尺寸差异很大时,影响会增大。例如,
int[128][2]
实例需要3,600个字节。与int[256]
实例使用的1,040字节(具有相同容量)相比,3,600字节表示246%的开销。在byte[256][1]
的极端情况下,开销因子几乎为19!将其与C / C ++情况相比较,在该情况下,相同的语法不会增加任何存储开销。
String
:String
的内存增长跟踪其内部字符数组的增长。但是,String
类会增加另外24个字节的开销。对于大小为10个字符或更少的非空
String
,相对于有用负载的额外开销成本(每个字符2个字节加上长度为4个字节),范围从100到400%。
考虑这个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
}
一个简单的总和表明X
的实例将使用17个字节。但是,由于对齐(也称为填充),JVM以8个字节的倍数分配内存,因此它将分配24个字节而不是17个字节。
答案 1 :(得分:29)
这取决于架构/ 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在标题之后和填充边界之前就已压缩。
代码示例:
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>
答案 2 :(得分:27)
每个对象对其关联的监视器和类型信息以及字段本身都有一定的开销。除此之外,字段可以布局得很多但是JVM认为合适(我相信) - 但是作为shown in another answer,至少一些 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个字节。
无论哪种方式,“单个大对象”至少与多个小对象一样有效 - 对于像这样的简单情况。
答案 3 :(得分:5)
不,注册一个对象也需要一些内存。具有1个属性的100个对象将占用更多内存。
答案 4 :(得分:5)
似乎每个对象在32位系统上的开销为16字节(在64位系统上为24字节)。
http://algs4.cs.princeton.edu/14analysis/是一个很好的信息来源。许多好的例子中有一个例子如下。
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf也非常有用,例如:
答案 5 :(得分:5)
一个具有100个属性的对象占用的内存空间是否与100个对象相同,每个属性有一个属性?
没有
为对象分配了多少内存?
添加属性时会使用多少额外空间?
答案 6 :(得分:4)
程序的总使用/可用内存可以通过
在程序中获得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));
}
}
答案 7 :(得分:3)
我从另一个答案中提到的java.lang.instrument.Instrumentation方法得到了非常好的结果。有关其使用的良好示例,请参阅JavaSpecialists的简报中的条目 Instrumentation Memory Counter 和SourceForge上的java.sizeOf库。
答案 8 :(得分:3)
问题将是一个非常广泛的问题。
它取决于类变量,或者您可以在java中调用状态内存使用。
它还有一些标题和引用的额外内存要求。
Java对象使用的堆内存包括
原始字段的内存,根据它们的大小(参见下面的原始类型的大小);
参考字段的内存(每个4字节);
一个对象标题,由几个字节的“内务”信息组成;
java中的对象还需要一些“内务”信息,例如记录对象的类,ID和状态标志,例如对象当前是否可访问,当前是同步锁定等。
Java对象标头大小因32位和64位jvm而异。
虽然这些是主要的内存消费者,但jvm还需要额外的字段,有时候需要对齐代码e.t.c。
原始类型的大小
布尔&amp;字节 - 1
char&amp;简短 - 2
int&amp;漂浮 - 4
长&amp;双重 - 8
答案 9 :(得分:2)
如果它对任何人都有用,您可以从我的网站下载一个小Java agent for querying the memory usage of an object。它还可以让您查询“深层”内存使用情况。
答案 10 :(得分:1)
不,100个小对象需要的信息(内存)比一个大的更多。
答案 11 :(得分:0)