Java 这种情况下,为什么原始数据类型比引用数据类型消耗更多内存?
Why does primitive data type consume much memory than reference data type for this scenario in Java?
我试图检查程序的内存消耗。在检查的过程中,我发现了一些有趣的事情。
我创建了一个负载 class,其中包含一些字段。
class Load {
String name;
String title;
long id;
}
我创建了 500000 个 Load 对象并将它们添加到 ArrayList。我发现,它占用了大约 18 MB
内存。
然后,我修改了加载 class 并使用 reference
类型 Long。
class Load {
String name;
String title;
Long id;
}
再次创建了 500000 个 Load 对象并将它们添加到 ArrayList。有趣的是,这次占用的内存比上一次少。大约 14 MB。
运行 测试更改 os 和 JVM 版本。找到以下结果。
OS: Windows 10 Pro 64 bit
JDK: 11 64bit
Object Created | Load Object | Memory | Load Object | Memory
------------------------------------------------------------------------------
1. 500000 | With primitive long | 18 MB | With reference Long | 14 MB
2. 900000 | | 32 MB | | 26 MB
3. 1500000 | | 53 MB | | 41 MB
OS: macOS Big Sur 64 bit
JDK: 8 64bit
Object Created | Load Object | Memory | Load Object | Memory
------------------------------------------------------------------------------
1. 500000 | With primitive long | 18 MB | With reference Long | 14 MB
2. 900000 | | 32 MB | | 26 MB
3. 1500000 | | 53 MB | | 41 MB
令人惊讶的是,在所有这些测试运行中,Object contains primitive types long 比 Object contains reference Long 消耗更多的内存。
我的问题是,为什么原始类型在这种情况下占用更多内存?
内存测试器代码:
public class MemoryChecker {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
List<Load> list = new ArrayList<Load>();
for (int i = 0; i <= 500000
; i++) {
list.add(new Load("Jim", "Knopf", 11L));
}
// 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 megabytes: " + bytesToMegabytes(memory));
}
}
完整代码git repo.
最大的primitive
可以容纳8 bytes
。
每个 Object 至少有 12 bytes
(默认 64 位 VM 和相对较小的堆)headers.自动使它比基元大。
据我所知,有一个很好的库是正确的,叫做 jol
。 here is a related 问题。
jol
set-up和run the samples很容易理解您感兴趣的实际数字。
对于 32 位 JVM,以及具有 CompressedOOPs 功能的 64 位 JVM(HotSpot JVM 支持并默认启用),与 8 字节相比,引用仅占用 4 字节long
.
即使您使用实际对象初始化引用,当对象 共享 时,它也可能消耗更少的内存。这适用于常量的 autoboxing:
If the value p being boxed is the result of evaluating a constant expression (§15.29) of type boolean
, byte
, char
, short
, int
, or long
, and the result is true
, false
, a character in the range '\u0000'
to '\u007f'
inclusive, or an integer in the range -128
to 127
inclusive, then let a
and b
be the results of any two boxing conversions of p
. It is always the case that a == b
.
但也适用于一般情况下以 Long.valueOf(long)
结束的所有操作。
This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
当然,如果你创建了很多非共享Long
对象,它们会比原始long
消耗更多的内存。如果您使用很多不同的值,即使可能共享它们也无济于事。
由于'Long id'没有初始化,所以它只占用4/8字节(取决于VM和OS),其中'long id'默认用0L初始化,占用8字节。
我试图检查程序的内存消耗。在检查的过程中,我发现了一些有趣的事情。
我创建了一个负载 class,其中包含一些字段。
class Load {
String name;
String title;
long id;
}
我创建了 500000 个 Load 对象并将它们添加到 ArrayList。我发现,它占用了大约 18 MB
内存。
然后,我修改了加载 class 并使用 reference
类型 Long。
class Load {
String name;
String title;
Long id;
}
再次创建了 500000 个 Load 对象并将它们添加到 ArrayList。有趣的是,这次占用的内存比上一次少。大约 14 MB。
运行 测试更改 os 和 JVM 版本。找到以下结果。
OS: Windows 10 Pro 64 bit
JDK: 11 64bit
Object Created | Load Object | Memory | Load Object | Memory
------------------------------------------------------------------------------
1. 500000 | With primitive long | 18 MB | With reference Long | 14 MB
2. 900000 | | 32 MB | | 26 MB
3. 1500000 | | 53 MB | | 41 MB
OS: macOS Big Sur 64 bit
JDK: 8 64bit
Object Created | Load Object | Memory | Load Object | Memory
------------------------------------------------------------------------------
1. 500000 | With primitive long | 18 MB | With reference Long | 14 MB
2. 900000 | | 32 MB | | 26 MB
3. 1500000 | | 53 MB | | 41 MB
令人惊讶的是,在所有这些测试运行中,Object contains primitive types long 比 Object contains reference Long 消耗更多的内存。
我的问题是,为什么原始类型在这种情况下占用更多内存?
内存测试器代码:
public class MemoryChecker {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
List<Load> list = new ArrayList<Load>();
for (int i = 0; i <= 500000
; i++) {
list.add(new Load("Jim", "Knopf", 11L));
}
// 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 megabytes: " + bytesToMegabytes(memory));
}
}
完整代码git repo.
最大的
primitive
可以容纳8 bytes
。每个 Object 至少有
12 bytes
(默认 64 位 VM 和相对较小的堆)headers.自动使它比基元大。
据我所知,有一个很好的库是正确的,叫做 jol
。 here is a related 问题。
jol
set-up和run the samples很容易理解您感兴趣的实际数字。
对于 32 位 JVM,以及具有 CompressedOOPs 功能的 64 位 JVM(HotSpot JVM 支持并默认启用),与 8 字节相比,引用仅占用 4 字节long
.
即使您使用实际对象初始化引用,当对象 共享 时,它也可能消耗更少的内存。这适用于常量的 autoboxing:
If the value p being boxed is the result of evaluating a constant expression (§15.29) of type
boolean
,byte
,char
,short
,int
, orlong
, and the result istrue
,false
, a character in the range'\u0000'
to'\u007f'
inclusive, or an integer in the range-128
to127
inclusive, then leta
andb
be the results of any two boxing conversions ofp
. It is always the case thata == b
.
但也适用于一般情况下以 Long.valueOf(long)
结束的所有操作。
This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
当然,如果你创建了很多非共享Long
对象,它们会比原始long
消耗更多的内存。如果您使用很多不同的值,即使可能共享它们也无济于事。
由于'Long id'没有初始化,所以它只占用4/8字节(取决于VM和OS),其中'long id'默认用0L初始化,占用8字节。