为啥没有定义 Java 的布尔原始大小?

Posted

技术标签:

【中文标题】为啥没有定义 Java 的布尔原始大小?【英文标题】:Why is Java's boolean primitive size not defined?为什么没有定义 Java 的布尔原始大小? 【发布时间】:2010-12-26 19:28:50 【问题描述】:

Java Virtual Machine Specification 表示对布尔 primitive 类型的支持有限。

没有专门用于布尔值操作的 Java 虚拟机指令。相反,Java 编程语言中对布尔值进行操作的表达式被编译为使用 Java 虚拟机 int 数据类型的值。

以上暗示(尽管我可能误解了它)在对布尔值进行操作时使用 int 数据类型,但这是一个 32 位内存构造。鉴于布尔值仅代表 1 位信息:

为什么不将字节或短类型用作布尔值而不是 int 的代理? 对于任何给定的 JVM,什么是准确找出用于存储布尔类型的内存量的最可靠方法是什么?

【问题讨论】:

【参考方案1】:

简短回答:是的,布尔值作为 32 位实体进行操作,但布尔数组每个元素使用 1 个字节。

更长的答案:JVM 使用 32 位堆栈单元,用于保存局部变量、方法参数和表达式值。小于 1 个单元的基元被填充,大于 32 位(长和双)的基元占用 2 个单元。这种技术最大限度地减少了操作码的数量,但确实有一些特殊的副作用(例如需要屏蔽字节)。

存储在数组中的基元可能使用少于 32 位,并且有不同的操作码可以从数组中加载和存储基元值。布尔值和字节值都使用 baloadbastore 操作码,这意味着布尔数组每个元素占用 1 个字节。

就内存对象布局而言,这包含在“私有实现”rules 中,它可以是 1 位、1 字节,或者如另一张海报所述,与 64 位双字对齐边界。最有可能的是,它采用底层硬件的基本字长(32 位或 64 位)。


就最小化布尔值使用的空间量而言:对于大多数应用程序来说,这确实不是问题。堆栈帧(保存局部变量和方法参数)不是很大,在大方案中,对象中的离散布尔值也不是那么大。如果您有很多带有大量布尔值的对象,那么您可以使用通过 getter 和 setter 管理的位域。但是,您将在 CPU 时间上付出代价,这可能比在内存中的代价更大。

【讨论】:

对于布尔/字节类成员,它们也是 4 个字节是否也是如此?类实例在堆栈上作为一个整体分配,所以我可以想象,JVM 可能应该为每个布尔/字节成员使用 1 个字节,最后为完整的类实例进行 4 字节对齐。是这样吗? (如果您有证明这一点的参考资料,请分享) @dma_k:正如我在回复中指出的,类实例的布局取决于实现。但是,请注意,类实例不存储在堆栈中,它们存储在堆中(尽管您会看到一些对 JDK 7“逃逸分析”将对象从堆栈移动到堆的引用,但情况似乎并非如此;请参阅 java.sun.com/javase/7/docs/technotes/guides/vm/...) 有时打包布尔值实际上可能更快。每当缓存大小很重要时,打包东西可能会更好。例如,分段素筛以 32 kB(L1 缓存大小)的块工作,比非分段筛快得多。块之间存在一些开销,并且通过打包,您支付开销的频率降低了八倍。我还没量过。【参考方案2】:

继承层次结构中某处的单个布尔值最多可以使用 8 个字节!这是由于填充。更多详情可在How much memory is used by my Java object?:

回到如何 布尔值消耗很多,是的 消耗至少一个字节,但由于 对齐规则它可能会消耗很多 更多的。恕我直言,更有趣的是 知道 boolean[] 会消耗一个 每个条目的字节而不是一位,加上 由于对齐和为 数组的大小字段。有 大领域的图算法 位很有用,你需要 请注意,如果您使用 boolean[] 几乎正好需要 8 倍以上 内存比实际需要(1 字节 与 1 位相比)。

【讨论】:

无论如何如何使用布尔值[]? boolean[] 可用于掩码。不过,有时 BitSet 会更好,因为它有一些有用的方法。【参考方案3】:

Java in a Nutshell 的第 5 版 (O'Reilly) 说布尔原始类型是 1 个字节。根据堆检查显示的内容,这可能是错误的。我想知道大多数 JVM 是否存在为变量分配少于一个字节的问题。

【讨论】:

【参考方案4】:

布尔映射是在考虑到 32 位 CPU 的情况下完成的。 int 值有 32 位,因此可以在一次操作中处理。

这是来自Peter Norvig's Java IAQ: Infrequently Answered Questions 的解决方案,用于测量尺寸(有些不精确):

static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end =  runtime.freememory();
System.out.println("That took " + (start-end) + " bytes.");

【讨论】:

由于本次对话是关于原语的,因此您必须在测试时发挥创造力,因为原语不会存储在堆中,除非它们是实例或数组上的字段。这些都没有回答 Java 将如何选择将其存储在堆栈中的问题。【参考方案5】:

CPU 以特定的数据类型长度运行。对于 32 位 CPU,它们的长度为 32 位,因此在 Java 中称为“int”。在 CPU 可以处理之前,必须将低于或高于此长度的所有内容填充或拆分为该长度。这不会花费太多时间,但如果您需要 2 个 CPU 周期而不是 1 个用于基本操作,这意味着成本/时间翻倍。

此规范专用于 32 位 CPU,以便它们可以使用其本机数据类型处理布尔值。

这里你只能有一个:速度或内存 - SUN 决定速度。

【讨论】:

【参考方案6】:

我们无法确定布尔数据类型的确切大小。 它取决于虚拟机或因操作系统而异。

【讨论】:

【参考方案7】:

为什么不制作一个这样的 .java 文件:

空.java

class Empty

还有这样的一类:

NotEmpty.java

class NotEmpty
   boolean b;

编译它们并用十六进制编辑器比较 .class 文件。

【讨论】:

这完全是另一个指标,与内存中原始布尔类型的大小无关。

以上是关于为啥没有定义 Java 的布尔原始大小?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在java中不允许没有括号的赋值和布尔运算符

为啥 Java 和 C# 没有到布尔值的隐式转换?

C#中的原始布尔大小

为啥 MySQL 不定义布尔数据类型?

Java:将原始布尔值转换为原始 int (0,1) 的好方法

java布尔值在被调用方法中没有改变[重复]