为什么Java对象必须是8的倍数?

Nan*_*ndo 18 java object padding

我知道Java使用填充; 对象必须是8个字节的倍数.但是,我没有看到它的目的.它是干什么用的?它的主要目的究竟是什么?

U2E*_*EF1 18

它的目的是对齐,允许以某些空间为代价更快地访问内存.如果数据未对齐,则处理器需要在加载内存后进行一些移位以访问它.

此外,垃圾收集简化(并加速),最小分配单元的大小.

Java不太可能需要8个字节(64位系统除外),但由于在创建Java时32位架构是常态,因此Java标准中可能需要4字节对齐.

  • @user1110725,请仔细阅读。对齐不仅用于垃圾收集,还用于一般的内存访问。 (2认同)
  • Java 标准不需要任何对齐,如果规范明确了这样的实现细节,那就会很奇怪。Sun 的实现几乎从一开始就使用 8 字节对齐,这并不奇怪,因为这种对齐方式在大多数操作系统的内存管理中也是最先进的。即使是老式的 Amiga500 操作系统也使用 8 字节对齐来进行内存分配。 (2认同)

Mat*_*ley 8

接受的答案是推测(但部分正确).这是真正的答案.

首先,对于@ U2EF1而言,8字节边界的好处之一是8字节是大多数处理器上的最佳访问.但是,决定还有更多.

如果你有32位引用,你可以寻址最多2 ^ 32或4 GB的内存(实际上你会得到更少,更像是3.5 GB).如果你有64位引用,你可以寻址2 ^ 64,这是terrabytes的内存.但是,对于64位引用,一切都会变慢并占用更多空间.这是由于处理64位的32位处理器的开销,并且由于更少的空间和更多的垃圾收集,所有处理器上的GC周期更多.

因此,创建者采取了中间立场并决定使用35位引用,这允许最多2 ^ 35或32 GB的内存并占用更少的空间,因此具有32位引用的相同性能优势.这是通过采用32位参考并在读取时将其向左移位3位并在存储参考时将其右移3位来完成的.这意味着所有对象必须在2 ^ 3个边界(8个字节)上对齐.这些被称为压缩普通对象指针或压缩oops.

为什么不使用36位引用来访问64 GB内存?嗯,这是一个权衡.你需要大量的浪费空间来进行16字节对齐,据我所知,绝大多数处理器都没有从16字节对齐中获得速度优势,而不是8字节对齐.

请注意,除非最大内存设置为高于4 GB,否则JVM不会使用压缩oops,默认情况下不会.你可以用-XX:+UsedCompressedOops旗帜实际启用它们.

这是在32位虚拟机的当天,以便在64位系统上提供额外的可用内存.据我所知,64位虚拟机没有限制.

来源:Java性能:权威指南,第8章

  • 8字节对齐的决定比压缩oops功能要早得多,并且也适用于不支持超过2GB的32Bit JVM。您对“ *未来的JVM将支持64位引用*”的猜测没有任何意义,因为所有64位JVM *都*都支持64位引用。当堆大于32GB或指定`-XX:-UsedCompressedOops`或使用不具有压缩oops功能的旧版JVM时,将使用它们。 (2认同)
  • 只是为了澄清:当引入64位JVM时,真正的64位指针是常态(返回jdk1.4).32位JVM不支持超过2GB的堆大小.所以当你想要超过2GB的堆时,你必须使用64位JVM,并且由于指针越大,内存消耗越大.然后,在Java 6中,添加了压缩oops作为优化功能,以允许在堆小于32GB时使用较小的指针.它始终是一个可选功能,您可以关闭它. (2认同)
  • @spilot:如前所述,*碎片*,仅举一个例子。由于没有 GC,因此不存在复制收集器之类的东西,也没有压缩,因此,以与分配不同的顺序释放对象/数据结构会留下可用内存的空洞,如果后续分配,则必须跟踪并重新分配这些空洞一种策略是将必要的信息存储在空闲内存本身中,因此在 32 位系统上,8 个字节足以保存空闲内存块的大小和指向下一个空闲块的指针。 (2认同)
  • @spilot 在《垃圾收集手册》(第 97 页)中有一些关于对齐和其他注意事项的讨论。 (2认同)