7

在 C 和 C++ 中,不允许编译器对结构的数据成员进行重新排序,因此如果您对它们的排序方式不小心,最终会浪费空间。例如:

struct S {
    int i;
    void *p;
    int i2;
};

在具有 32 位ints 和 64 位指针的平台上,i将首先放置,然后是 32 位填充,以便p可以 64 位对齐。i2然后占用下一个字的一半,然后是另外 32 位的填充。生成的结构是 24 字节长,而如果p首先声明,它将只有 16 字节长。如果数组中有很多这些结构,查找和删除填充有时可能是一项重要的优化,以节省内存并减少缓存流失。

我很想知道Java是否具有相同的功能。未装箱的类型(例如intboolean)是否与引用的大小相同或更小?如果它们更小,是否允许编译器重新排序它们以避免插入填充以对齐后续字段?最后,如果是的话,是否有任何编译器这样做?

我现在对此没有特别的优化需求,我只是想知道在选择声明字段的顺序时是否应该记住这一点,就像我在 C 中所做的那样。

4

4 回答 4

4

在java中有这样的问题。编译器处理变量声明,程序员无法控制在内存中分配它们的位置

于 2012-10-15T11:11:57.360 回答
4

int类型始终为 32 位,即使在 64 位 JVM 中,引用也通常为 32 位。

不利的一面是,Java 在每个对象的开头都有一个 8-12 字节的标头,并使用 8 字节对齐。顺便说一句,一些 C++ 环境有 16 字节对齐。

未装箱的类型(例如 int 和 boolean)是否与引用的大小相同或更小?

您可以期望它们对于 boolean、byte、char 和 short 来说更小,但是对于 long 和 double 来说,原语可能比引用更大。

如果它们更小,是否允许编译器重新排序它们以避免插入填充以对齐后续字段?

JIT 可以重新组织字段,甚至优化它们。

最后,如果是的话,是否有任何编译器这样做?

编译几乎没有优化,javac查看字节码会给你一点关于运行时会发生什么的线索。JIT 可以任意选择优化对象中的字段。

我只是想知道在选择声明字段的顺序时是否应该记住这一点,就像我在 C 中所做的那样。

恕我直言,您可以假设您在 C 中使用的几乎所有优化技巧都不再适用于 Java。在少数这样做的人中,它们可能并不完全相同。

您应该假设 JIT 将根据需要优化代码并使用分析器来确定您是否以及何时遇到问题。然后才考虑出于性能原因更改代码。

于 2012-10-15T11:20:27.220 回答
3

Java 没有结构,只有类。Java 中的类并不一定意味着该类也在 C++ 后端。你甚至不知道 JVM 是否是用 C/C++ 实现的。它可以用任何其他语言。

但是对于 C++ JVM,我认为他们会尝试优化。原始(它们不称为未装箱)类型,例如 int 或 boolean,通常映射到它们的原始对应物(就我的 JNI 经验而言),但由于没有结构,它们不需要排列。

我几乎可以肯定,重新排列类字段不会对内存消耗产生任何影响。只是因为它们没有映射到结构。

于 2012-10-15T11:12:31.400 回答
0

正如其他人指出的那样,您确实无法控制 java 中的这种详细程度。但是,如果您真的需要对内存进行如此多的控制,您可以通过索引到 byte[] 甚至直接ByteBuffer中的堆外来自己管理它。

于 2012-10-15T11:49:15.080 回答