Java 中装箱原语的存储成本是多少?

发布于 2024-12-29 12:26:20 字数 410 浏览 1 评论 0原文

Java 中像 java.lang.Integerjava.lang.Character 这样的装箱原语有多大(以字节为单位)?

一个int是4个字节,一个典型的指针也是4个字节(如果没有被JVM压缩)。整数(没有缓存)的成本是 4 字节 + 4 字节 = 8 字节吗?盒子对象中是否还有更多隐藏字段或与对象有关的额外开销(即,是否存在我不知道的对象的一般成本?)。

我对缓存问题不感兴趣。我知道JVM会缓存一定范围内的整数。

人们可以重新表述这个问题:装箱值与原始值所用内存量相乘的最大因子是多少?

编辑:我确实知道 JVM 存在多种实现。典型 32 位 HotSpot 实施的典型成本是多少?

How large, in bytes, is a boxed primitive like java.lang.Integer or java.lang.Character in Java?

An int is 4 bytes, a typical pointer is also 4 byte (if not compressed by the JVM). Is the cost for an Integer (without caching) thus 4 bytes + 4 bytes = 8 bytes? Are there any more hidden fields within the box-object or additional overhead incurred regarding objects (i.e. is there a general cost for objects that I'm not aware of?).

I'm not interested in caching issues. I know that Integers within a certain range are cached by the JVM.

One could rephrase the question: What is the maximum factor to be multiplied on the amount of memory used for boxed values versus primitive values?

EDIT: I do understand that multiple implementations of the JVM exist. What is the typical cost in a typical 32-bit HotSpot Implementation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

傲世九天 2025-01-05 12:26:20

这是实现定义的,所以没有具体的答案。但我应该能够为 Hotspot 回答这个问题。

您需要知道的是:Hotspot 始终在 8 字节边界上对齐对象。此外,每个对象都有 2 个单词的开销。 [1]

如果我们把它们放在一起,我们得到:

32 位 VM:4 字节整数 + 2 个字对象头 = 12 字节。这不是 8 的倍数,因此 1 个整数的成本是 8 的下一个倍数:16 字节。

64位VM:4字节整数+2个字=20字节。再次四舍五入:24 字节大小。

引用的大小显然不会影响对象本身的大小,除非它引用了其他对象,而简单的 int 包装器则不然。如果可以的话,对于 32 位,我们的每个引用将有 4 个字节,对于 64 位 JVM 上的现代 JVM 上的 CompressedOops 堆 <= 32gb,每个引用将有 4 个字节(否则为 8 个字节)。

[1]有兴趣的可以看share/vm/oops/oop.hpp中的代码

This is implementation defined, so there's no specific answer. But I should be able to answer it for Hotspot.

What you need to know is: Hotspot always aligns objects on 8byte boundaries. Furthermore there are 2 words overhead for each and every object. [1]

If we put this together we get:

32bit VM: 4byte integer + 2 words object header = 12bytes. That's no multiple of 8 so as a result the cost for 1 integer is the next multiple of 8: 16byte.

64bit VM: 4byte integer + 2 words = 20bytes. Rounding up again: 24byte size.

The size of a reference obviously does not play into the size of an object itself, except if it has references to other objects which isn't the case for a simple int wrapper. If it would, we'd have 4byte per reference for 32bit and 4byte for heaps <= 32gb with CompressedOops on modern JVMs (otherwise 8byte) for 64bit JVMs.

[1] Interested people can look at the code in share/vm/oops/oop.hpp

豆芽 2025-01-05 12:26:20

不仅如此。

每个对象引用都有额外的开销,例如类引用。不仅如此,您的 4 字节指针还不太准确。它是一个引用,因此它是一个 ID 加上一个指针,如果您使用的是 64 位 JVM,则该指针可能是 8 个字节。

VM 实现似乎也存在差异。确定这一点的最佳方法是将其拉入分析器中。

我的(超级SWAG)估计是。
对象引用 16 字节(64 位 JVM)
类参考 16 字节
原始值 4 个字节(假设为 int。)
全部的。 36 字节。

编辑:既然您指定了 32 位 JVM,那么使用上面相同的数学计算,我的 SWAG 将是 20 个字节。

It's more than that.

Every object reference has additional overhead, such as a Class reference. Not only that, your 4-byte pointer isn't quite accurate. It's a reference, so it's an ID plus a pointer, AND that pointer may be 8 bytes if you are on a 64 bit JVM.

There also appear to be VM implementation differences. Best way to be sure on this would be to pull it up in a profiler.

My (Super SWAG) estimate would be.
Object reference 16 bytes (64 bit JVM)
Class reference 16 bytes
primitive value 4 bytes (Assuming int.)
Total. 36 bytes.

EDIT: Now that your specify 32-bit JVM my SWAG would be 20 bytes using same math above.

左岸枫 2025-01-05 12:26:20

这些答案的一个非常小的补充是,盒装基元发生了一些重复数据删除。例如,Integer::valueOf(int) 使用 java.lang.IntegerCache,它使用值在 -128..127 范围内的 Integer 实例。 。因此,您具有上述盒装对象的尺寸,但并非每个对象都是单独的对象。

One very small addition for these answers is that there is some deduplication happening for boxed primitives. For example, Integer::valueOf(int) uses java.lang.IntegerCache that uses Integer instances with values in range -128..127. So, you have above mentioned sizes for boxed objects, but not each of them would be a separate object.

罪#恶を代价 2025-01-05 12:26:20

我知道这并不能完全回答您关于盒装基元的存储成本的问题,但我从您的问题中感觉到您在质疑是否有必要使用它们。

以下是 Joshua Bloch 所著的《Effective Java》(第二版)的摘录,应该可以帮助您做出决定:

那么什么时候应该使用装箱基元呢?它们有多种合法用途。这
第一个是集合中的元素、键和值。您不能将基元放入集合中,因此您被迫使用盒装基元。这是更一般情况的一种特殊情况。您必须使用装箱基元作为参数化类型中的类型参数(第 5 章),因为该语言不允许您使用基元。例如,您不能将变量声明为 Thread Local 类型,因此必须使用 ThreadLocal 代替。最后,在进行反射方法调用时必须使用装箱基元(第 53 项)。`

总之,只要有选择,就优先使用基元而不是盒装基元。原始类型更简单、更快。如果您必须使用盒装基元,请小心!自动装箱减少了使用装箱原语的冗长性,但并没有减少危险。当您的程序使用 == 运算符比较两个装箱基元时,它会进行身份比较,这几乎肯定不是您想要的。当您的程序执行涉及装箱和拆箱基元的混合类型计算时,它会进行拆箱,并且当您的程序进行拆箱时,它可能会抛出 NullPointerException。最后,当您的程序装箱原始值时,可能会导致昂贵且不必要的对象创建。

I know this doesn't exactly answer your question on the storage cost of boxed primitives, but I sense from your question that you are questioning whether or not your use of them is warranted.

Here is an excerpt from Effective Java (2nd Edition) by Joshua Bloch that should help you decide:

So when should you use boxed primitives? They have several legitimate uses. The
first is as elements, keys, and values in collections. You can’t put primitives in collections, so you’re forced to use boxed primitives. This is a special case of a more general one. You must use boxed primitives as type parameters in parameterized types (Chapter 5), because the language does not permit you to use primitives. For example, you cannot declare a variable to be of type Thread Local<int>, so you must use ThreadLocal<Integer> instead. Finally, you must use boxed primitives when making reflective method invocations (Item 53).`

In summary, use primitives in preference to boxed primitives whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw a NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文