java - 为什么Java boolean 值必须至少为1个字节?

标签 java memory memory-management low-level

众所周知,C++ bool的大小必须至少为1个字节,以便可以为每个[https://stackoverflow.com/a/2064565/7154924]创建指针。但是在Java中没有指向原始类型的指针。但是,它们仍然占用至少1个字节的 [https://stackoverflow.com/a/383597/7154924]

为什么会这样-为什么Java boolean的大小不能为1位?抛开计算时间,如果一个人有一个较大的boolean数组,那么肯定可以设想一种编译器,该编译器会进行适当的移位以检索与boolean值相对应的单个位吗?

最佳答案

没有理由为什么 boolean 值必须为一个字节。实际上,在某些情况下, boolean 值的有效大小可能已经不是1个字节了:当在堆栈或对象中紧挨着大于1个字节的其他元素排成一行时,它们可能会更大(即,添加它们到一个对象可能会导致大小增加一个以上的字节)。

任何JVM都可以自由地将 boolean 值实现为1位,但是据目前尚无人选择这样做,这可能主要是因为:

  • 访问一位通常比访问字节更昂贵,尤其是在写入时。

    要读取位,使用“经典RISC”指令集的CPU通常需要一个附加的and指令,以从 boolean 位的压缩字节(或较大字)中提取相关位。有些人甚至可能需要一条额外的指令才能将常量加载到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数组建立索引的情况下,请测试instructions, including specific bit-test instructions taking a variable position such as bt`。在这两种表示形式中,此类CPU可能具有相似的读取性能。

    写入更糟:现在,您需要读取值,修改适当的位并将其写回,而不是简单的字节写入来设置boolean值。某些平台(例如x86)具有内存源和目标RMW指令(例如andor)会有所帮助,但它们仍然比普通写入昂贵得多。在最坏的情况下,重复写入同一元素将导致通过内存的依赖链,这可能会使您的代码速度降低一个数量级(一系列普通存储无法形成依赖链)。

    更糟糕的是,上面的write方法完全是线程不安全的。工作在“独立” boolean 值上的两个线程可能会相互破坏,因此运行时将不得不使用原子更新操作,只是为无法证明对象在线程本地的任何字段写一些位。
  • 数组外的空间节省通常很小,并且通常为零:对齐问题意味着一个位通常最终会占用与堆栈或对象布局中的字节相同的空间。仅当您在堆栈或对象上具有许多原始boolean值时,您才会看到节余(例如,对象通常对齐8字节边界,因此,如果您的对象的非 boolean 字段为int或更大,则可以至少需要4个boolean值来节省空间,而且通常需要8)。
  • 这为boolean数组中的位表示形式boolean留下了最后一个“大赢家”,对于大型数组,您可以在其中节省8倍的渐近空间。实际上,这种情况在C++世界中具有足够的动机,因此vector<bool>有一个“特殊”实现,其中每个bool都花了一点点-由于所有必需的特殊情况和非直觉的行为(而且经常使用),头痛无休止作为目前无法删除的功能失调的示例)。

    如果不是用于内存模型,我可以想象一个世界,其中Java
    以位方式实现boolean数组。他们没有
    我认为与vector<bool>相同的问题(主要是由于JIT提供了额外的抽象层,并且还因为数组提供了比vector更为简单的接口(interface)),因此可以高效地完成此工作。虽然有讨厌的内存模型。该模型允许通过不同线程完成对不同数组元素的写入操作(即,出于内存模型的目的,它们充当自变量)。如果您将boolean实现为字节,则所有通用CPU都直接支持此功能,因为它们具有独立的字节访问权限。但是,没有CPU提供独立的位访问:您被困在原子操作上(x86提供lock bt*操作,但是速度很慢:其他平台的选择甚至更糟)。这将破坏任何实现为位数组的 boolean 数组的性能。

  • 最后,如上所述,稍微实现boolean会有很大的缺点-但是有什么好处呢?

    事实证明,如果用户真的想要 boolean 的位打包表示形式,那么他们可以自己这样做!他们可以将8个 boolean 值打包到对象的byte中(或32个值打包到int或其他任何值中)(这对于标志是常见的,等等),并且所生成的访问器代码的效率应该与JVM本地支持的boolean一样高效。 -按位。实际上,当您知道要为大量 boolean 值使用位数组表示形式时,可以简单地使用BitSet-它具有所需的表示形式,并且通过不提供任何线程安全保证来避免原子性问题。因此,通过将boolean实现为一个字节,可以避免上述所有问题,但是如果需要,仍可以让用户“选择加入”位级表示,而不会花费很多运行时间。

    关于java - 为什么Java boolean 值必须至少为1个字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47732906/

    相关文章:

    Java 8 Int Stream 使用 StringBuilder 收集

    c - Linux中的共享内存锁(C语言)

    c++ - Will process load into memory with 4 or 8 对齐规则

    不同类型的 C++ 模板错误

    php - Doctrine 查询内存使用情况

    java - 如何使用 Java 获取 Ubuntu 版本?

    java - 使用 SimpleDateFormat 但小时值倒退 3 小时

    python - 与倒数第二个相比,最后一个索引对 numpy 数组的访问时间的影响更大

    c++ - 在这种情况下我应该使用指针 vector 吗?

    java - java中向数组添加新元素