java - 在 Java 中,为什么原始数据类型比引用数据类型消耗更多内存?
问题描述
我试图检查程序的内存消耗。在检查过程中,我注意到了一些有趣的事情。
我创建了一个 Load 类,其中包含一些字段。
class Load {
String name;
String title;
long id;
}
我创建了 500000 个加载对象并将它们添加到 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 包含引用 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。
解决方案
对于 32 位 JVM,以及具有CompressedOOPs功能的 64 位 JVM(HotSpot 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
。如果您使用许多不同的值,即使潜在地共享它们也无济于事。
推荐阅读
- bash - Python - 解析为相对于脚本文件的绝对路径
- r - R - table() 返回重复的因素
- javascript - history.pushState 后前进按钮不起作用
- makefile - 创建共享库链接时的 Makefile 依赖项
- maven-plugin - Maven/Jacoco - 在成功的测试和 jacoco 数据文件合并后,如何获得合并报告?
- r - R中用于工作目录的可选参数解析
- php - Laravel 在 PHP 一般错误:1366 Incorrect integer value
- web-crawler - 指向 HdfsBolt 的 URL 内容
- symfony - 无法使用 SF2 表单删除具有多对多相关对象的实体
- swagger - 在示例值中隐藏字段,但不在 Swagger UI 中的请求正文模型中