成员订单是否会像在C或C ++中那样在Java中产生性能差异?

时间:2012-10-15 11:06:19

标签: java optimization bytecode

在C和C ++中,不允许编译器重新排序结构的数据成员,所以如果你不小心订购它们,你最终会浪费空间。例如:

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

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

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

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

4 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分: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将根据需要优化代码,并使用分析器来确定是否以及何时出现问题。然后才考虑出于性能原因而改变代码。

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:0)

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