Java 类的内存对齐

Posted

技术标签:

【中文标题】Java 类的内存对齐【英文标题】:Memory alignment of Java classes 【发布时间】:2017-11-12 02:41:07 【问题描述】:

假设我在一个 64 位机器上用gcc 编译一个 C 程序。我假设 sizeof(int) 是 8 个字节,sizeof(char) 是 1 个字节。

由于内存对齐,下面的struct:

struct example
    int a;
    char c;

实际上的大小不是 9 个字节,而是 16 个(两次sizeof(int)),因此它的起始地址和结束地址都可以是字大小的倍数(这里假设为 8 个字节)。

我想知道以下类在 Java 8 中会有多大:

class Node 
    int val;
    Node left, right;
    boolean flag;
 

我基本上不确定我们是否会以 8 字节或 4 字节的倍数对齐。

【问题讨论】:

【参考方案1】:

您可以使用jol 了解对象的确切布局。这是您的 Node 类程序的输出(在 Oracle JDK 1.8.0_121 64 位上):

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

org.example.Node object internals:
OFFSET  SIZE    TYPE DESCRIPTION                    VALUE
     0     4         (object header)                01 00 00 00 (00000001 00000000 00000000 00000000) (1)
     4     4         (object header)                00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     8     4         (object header)                70 22 01 f8 (01110000 00100010 00000001 11111000) (-134143376)
    12     4     int Node.val                       0
    16     1 boolean Node.flag                      false
    17     3         (alignment/padding gap)        N/A
    20     4    Node Node.left                      null
    24     4    Node Node.right                     null
    28     4         (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

所以,对齐是 8 个字节。

注意,这是特定于平台的。您不应该过多依赖这些信息。

【讨论】:

啊,对齐信息就在那里。太好了,谢谢! 包含使用的命令会很有用。我可以按照链接的文档使其适用于 JDK 类,但尝试为像 Node 这样的简单 .class 文件重现此文件我得到ClassNotFoundException。困惑。 @LukeUsherwood 你看到this了吗?我用的是 Java 8。 我浏览了一些示例,但印象中您可以按照链接 wiki 的“用作命令行工具”部分中的示例在命令行上执行此操作。 (我找不到像-cp 这样的标志,所以我想也许工作目录设置得当......)所以你编写了代码并以编程方式打印了这些数据,我接受了吗? 我使用java -cp "jol-cli-0.9-full.jar:." org.openjdk.jol.Main internals package.ClassUnderTest 创建了可比较的表。我对结果有点困惑:看到上面它是在 64 位机器/jvm 上执行的。类似乎与字长(8 字节)对齐。基元根本没有对齐。并且引用对齐到半字(4 个字节)。真的是这样吗?【参考方案2】:

ZhekaKozlov 的回答不正确。

至少在 JDK8 上的 Hotspot JVM 中,无论是 32 位 JVM 还是 64 位,对齐方式默认为 8 个字节。

(对不起,我没有足够的代表发表评论,所以我必须发布一个新答案)

在Oracle jdk-8u251 x86版本上试过,

您可以尝试使用选项XX:ObjectAlignmentInBytes=4 启动您的(热点)JVM,它会失败并出现错误:

ObjectAlignmentInBytes=4 必须大于或等于 8

有趣的旁注,如果你把它设置为9,它也会抱怨这个数字必须是2的幂。

同样在上面的 JVM 上运行这段代码 sn-p 没有任何明确的 VM 选项集,使用 jol,

System.out.println(VM.current().details());

它会打印出来

# Running 32-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

请注意,对齐大小未在 JVM 规范中定义,因此它可能因实现而异。

【讨论】:

你是绝对正确的。在 32 位 JVM 上,对象对齐也永远不会少于 8 个字节。这解释了here。我修正了我的答案。

以上是关于Java 类的内存对齐的主要内容,如果未能解决你的问题,请参考以下文章

内存对齐问题

内存对齐以及如何关闭内存对齐

开发宝典Java并发系列教程

内存对齐原理

关于C++的内存对齐

结构体内存对齐