Java中的.getClass()方法代价昂贵吗?

10

我正在将一个特定的数据处理算法从Java移植到C++。重新编写代码的原因是可移植性,它需要在没有Java环境的情况下运行。然而,作为一个附带好处,也期望有一些性能提升。

基本上,该算法从由相互指向的对象构成的图中读取数据,然后计算结果。在计算过程中会进行许多对象分配,所以这可能是导致减速的原因。问题是,目前C++代码的运行速度大约比旧的Java代码快10倍。这真的出乎意料。我只想看到50-60%的改进。

不幸的是,我不能在这里发布代码供分析。它有几千行,所以我不确定这是否方便。

问题在于,算法几乎完全相同。我唯一想到的主要区别是,在Java中有许多子类继承自一个超类,并且在计算过程中经常调用if(object.getClass() == daughterx.class),而在C++代码中仅使用了一个通用类(因为子类之间的差异很小),并且使用简单的整数比较例如if(object->type == 15)。

在Java中,Object.getClass()方法的开销有多大?当调用此方法时,低层级别实际上正在发生什么?


5
@xlc0212 也没有其他不懂Java的人! ;) - cheeken
3
说实话,这似乎不是一个问题。 废弃的代码值得你花时间进行基准测试和分析吗? 除非你这样做了,否则无法知道 .getClass() 是否成为瓶颈。 猜测并不是解决性能问题的有益方式,除非你能显著降低算法的总体复杂度(例如从N平方操作到对数N操作),并且数据集非常大。 - Merlyn Morgan-Graham
1
@MerlynMorgan-Graham 是的,这不是问题。这就是为什么我在闲暇时间在这里提问,而不是实际进行任何分析。 - mimicocotopus
1
@mimicocotopus: 没有剖析和基准测试,我们都只是在猜测。我认为你怀疑对象分配可能是对的。垃圾收集也可能很耗资源。 - Merlyn Morgan-Graham
6
@xlc0212 这位受尊敬的“我记得读过”的信息来源对这些问题很有权威性。根据我记得读过的任何基准测试结果,除非C++代码高度调整、工作量异常或(最有可能的情况是)Java代码不好,否则您不应该看到数量级差异。 - millimoose
显示剩余2条评论
3个回答

16
Java中的Object.getClass()方法有多昂贵? 基于我对非主流JVM实现方式的了解,它是廉价的。 该方法被调用时,底层到底发生了什么? 通常会执行以下步骤: 1. 从对象头部提取类索引(2或3条指令) 2. 从类索引查找类描述符(2或3条指令) 3. 从类描述符获取并返回Class对象引用(2或3条指令)
问题在于C++代码目前运行速度比旧的Java代码快10倍。 我认为性能瓶颈在其他地方。在得出结论之前,请尝试对Java代码进行分析。

6
虽然查找本身很便宜,但如果您有“if(getClass()== SomeClass.class)”的序列,则意味着存在重大的设计和性能问题。这表明您需要更多的面向对象设计,或者像OP所做的那样,合并这些类以便可以使用开关或类似功能。 - Peter Lawrey

9

当Java虚拟机没有完全预热时,10倍性能差异最有可能发生。如果不进行预热,即使在Java中也可能看到超过10倍的性能差异。我建议尝试以每批次10,000次运行,并忽略前几次运行。

public static void main(String... args) throws IOException {
    timeObjectGraph("First run", 1);
    timeObjectGraph("Second run", 2);
    timeObjectGraph("Next thousand", 1000);
    for (int i = 0; i < 5; i++)
        timeObjectGraph("Next ten thousand", 10000);
}

static int dontOptimiseAway = 0;

public static void timeObjectGraph(String desc, int runs) throws IOException {
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(Calendar.getInstance());
        oos.close();
        dontOptimiseAway = out.toByteArray().length;
    }
    long time = System.nanoTime() - start;
    System.out.printf("%s took an avg time of %,d ns%n", desc, time / runs);
}

打印

First run took an avg time of 37,509,488 ns
Second run took an avg time of 439,054 ns
Next thousand took an avg time of 185,242 ns
Next ten thousand took an avg time of 41,698 ns
Next ten thousand took an avg time of 19,981 ns
Next ten thousand took an avg time of 11,541 ns
Next ten thousand took an avg time of 13,451 ns
Next ten thousand took an avg time of 11,289 ns

从第一次到最后一次运行,性能提升了3000倍。


为什么要使用Calendar.getInstance()?首先,问题是关于getClass方法的。其次,Calendar是一个重量级类,创建它非常耗费资源,因此Calendar.getInstance()本身就很昂贵,而不是因为在其中调用了getClass方法。我认为这个例子不正确,或者你应该提供更清晰的注释。 - Cherry
2
@Cherry 我使用了Calendar和ObjectOutputStream,因为它们对于一小段代码来说都很昂贵,这提供了一个现实的例子,说明预热对于大量代码的影响有多大。 - Peter Lawrey

6

这很可能不是性能差异的唯一因素。不幸的是,如果没有更完整的代码情况,很难告诉你到底发生了什么。

根据我的经验,Java没有比C++慢10倍的理由。我建议您从分析器开始,找出问题所在,而不是猜测。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接