众所周知,C ++ bool
的大小必须至少为1个字节,以便为每个[https://stackoverflow.com/a/2064565/7154924]创建指针。但是Java中没有指向原始类型的指针。然而,他们仍然至少占用1个字节 [https://stackoverflow.com/a/383597/7154924]。
为什么会出现这种情况 - 为什么Java boolean
的大小不能为1位?除了计算时间,如果有一个大的boolean
数组,肯定可以设想一个编译器进行适当的移位以检索对应于boolean
值的单个位吗?
答案 0 :(得分:6)
没有理由为布尔必须大小为一个字节。事实上,在某些情况下,布尔可能已经不是1字节(有效)大小:当堆叠在一个大于1个字节的堆栈或对象中时,它们很可能是更大的(即,将它们添加到对象可能会导致大小增长超过一个字节)。
任何JVM都可以自由地实现1位布尔值,但据我们所知,没有人选择这样做,可能主要是因为:
访问一位通常比访问一个字节更昂贵,特别是在写入时。
要读一点,使用"经典RISC"指令集通常需要额外的and
指令来从布尔位的压缩字节(或更大字)中提取相关位。有些人甚至可能需要额外的指令来将常量加载到and
。在索引boolean, where the bit-index isn't fixed at compile-time, you'd need a variable shift. Some CPUs such as x86 have an easier time since they have memory source
test instructions, including specific bit-test instructions taking a variable position such as
bt`数组的情况下。在这两种表示中,这样的CPU可能具有相似的读取性能。
写入更糟糕:现在需要读取值,而不是简单的字节写入来设置boolean
值,修改相应的位并将其写回。某些平台(如x86)具有内存源和目标RMW指令,如and
和or
,这些指令会有所帮助,但这些指令仍然比普通写入要贵得多。在最坏的情况下,重复写入相同的元素将导致通过内存的依赖链,这可能会使代码减慢一个数量级(一系列普通存储不能形成依赖链)。
更糟糕的是,上面的write方法完全是线程不安全的。工作的两个线程"独立"布尔可能会互相破坏,因此运行时必须使用原子更新操作,只为该对象无法在线程本地证明的任何字段写一点。
数组之外的空间节省通常非常小,通常为零:对齐问题意味着单个位通常最终会占用堆栈上的字节或对象的布局中的相同空间。只有当您在堆栈上有许多原始boolean
值或对象时才能看到节省(例如,对象通常与8字节边界对齐,因此如果您有一个非布尔字段为{的对象{1}}或更大,您需要至少4个int
值才能节省空间,而且通常您需要8)。
这留下了最后剩余的"大赢#34;对于boolean
数组中的位表示boolean
,您可以为大型数组节省8倍的空间。事实上,这个案例在C ++世界中足够激励boolean
有一个特殊的&#34;每个vector<bool>
占一位的实现 - 由于所有必需的特殊情况和非直观行为而导致的永无止境的头痛源(并且通常用作不能删除的错误功能的示例)现在)。
如果它不是内存模型我可以想象一个Java的世界
以逐位方式实现bool
的数组。他们没有
与boolean
相同的问题(主要是因为JIT提供了额外的缩减层,也因为数组提供了比vector<bool>
更简单的接口),我认为它可以有效地完成。但是有一种讨厌的记忆模型。如果由不同的线程完成,该模型允许对不同数组元素的写入是安全的(即,它们作为存储器模型的独立变量)。如果将vector
实现为一个字节,所有常见的CPU都会直接支持这一点,因为它们具有独立的字节访问权限。但是没有CPU提供独立的位访问:你使用原子操作(x86提供boolean
操作),但这些操作很慢:其他平台的选项更糟糕。这将破坏作为位数组实现的任何布尔数组的性能。
最后,如上所述,实施lock bt*
会有一些重大缺点 - 但是好处呢?
事实证明,如果用户真的想要这个布局的比特包装表示,他们可以自己做!他们可以将8个布尔值打包到一个对象中的boolean
(或32个值到byte
或其他)中(这对于标志等是常见的),并且生成的访问者代码应该是高效的效率好像JVM本身支持boolean-as-bit。事实上,当你知道你想要一个大量布尔值的数组位表示时,你可以简单地使用int
- 它具有你想要的表示并回避原子问题不提供任何线程安全保证。因此,通过将BitSet
实现为一个字节,您可以回避上述所有问题,但仍然允许用户选择&#34;选择加入&#34;如果需要,可以进行位级表示,而不会有太多的运行时间惩罚。