作者:爱你无丶止境空间 | 来源:互联网 | 2023-09-12 08:23
我试图检查程序的内存消耗。在检查过程中,我注意到了一些有趣的事情。
我创建了一个 Load 类,其中包含一些字段。
class Load {
String name;
String title;
long id;
}
我创建了 500000 个 Load 对象并将它们添加到 ArrayList。我发现,它占用18 MB
了内存。
然后,我修改了 Load 类并使用了reference
Long 类型。
class Load {
String name;
String title;
Long id;
}
再次创建了 500000 个 Load 对象并将它们添加到 ArrayList。有趣的是,这一次它占用的内存比前一次少。它的方式是 14 MB。
运行测试更改操作系统和 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 比包含引用 Long 的 Object 消耗更多的内存。
我的问题是,为什么在这种情况下原始类型需要更多内存?
内存测试代码:
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 list = new ArrayList();
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。
回答
对于 32 位 JVM,以及具有CompressedOOPs功能(HotSpot JVM 支持并默认启用)的64 位 JVM ,引用仅消耗 4 个字节,而long
.
即使您使用实际对象初始化引用,共享对象时它也可能消耗较少的内存。这适用于常量的自动装箱:
如果值p被装箱是评估一个常量表达式(结果§15.29类型的)boolean
,byte
,char
,short
,int
,或long
,其结果是true
,false
在的范围内的字符'u0000'
,以'u007f'
包容,或在范围内的整数-128
,以127
包容,然后让a
和b
成为 的任意两个装箱转换的结果p
。情况总是如此a == b
。
但也适用Long.valueOf(long)
于一般情况下的所有操作。
此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。
当然,如果你创建了很多非共享Long
对象,它们会比原始对象消耗更多的内存long
。如果您使用许多不同的值,即使是潜在的共享它们也无济于事。