我正在查看此讨论:How slow are Java exceptions? 在进行实验时,发现如果我运行静态方法而不是实例方法,那么正常路径实际上比try-catch路径花费更多时间。
我正在做的是:创建一个无操作的静态foo()
方法,创建静态方法method1()
,正常地调用{100}次,另一个静态方法foo()
在try-catch块中调用method2()
100000000次。我看到的是,foo()
实际上花费的时间少于method2
。
有什么想法?
method1
答案 0 :(得分:2)
我试图重新创建您的测试代码,然后通过javap运行它。这些是在最后给出的,因此您不必滚动大文本块。
请注意,当VM没有完全没有执行优化时,字节码将按照下面的javap转储执行。因此,假设没有其他外部因素,method2()
的执行总是需要更长的时间,因为它包含一条额外的指令(line 11: goto 15
)。
当然,正如Joachim在下面提到的那样,“字节码对性能的描述很少。”
有很多标记可用于分析和启用/禁用JVM优化。在网上浏览一下。对于1.4.2,我发现这个link也适用于较新的JRE。
编辑添加:在支持的VM中,您可以使用以下VM标记-XX:-PrintCompilation
启用JIT跟踪输出。
javap输出:
Ryan-Schippers-MacBook-Pro-2:Miscellaneous work$ javap -c -classpath ./src SlowTryCatch
Compiled from "SlowTryCatch.java"
public class SlowTryCatch extends java.lang.Object{
public SlowTryCatch();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: return
public static void foo();
Code:
0: return
public static void method1();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: ldc #2; //int 100000000
5: if_icmpge 17
8: invokestatic #3; //Method foo:()V
11: iinc 0, 1
14: goto 2
17: return
public static void method2();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: ldc #2; //int 100000000
5: if_icmpge 21
8: invokestatic #3; //Method foo:()V
11: goto 15
14: astore_1
15: iinc 0, 1
18: goto 2
21: return
Exception table:
from to target type
8 11 14 Class java/lang/Exception
}
答案 1 :(得分:0)
我已经修改了一些代码,因此优化器不会删除代码。通过在Sun / Oracle JVM中运行它几次,我发现的是:
withNormal()
和withTry
之间的差异很小。这是预期的,因为没有抛出任何实际异常,并且没有finally
块。总而言之,我不希望使用异常的代码之间有任何显着差异,在Sun / Oracle JVM中运行时,很可能是来自HotSpot的噪音。
<强>更新强>
我使用-server
和-client
标志运行它,除了执行速度在我的机器中快一个数量级之外,上述观察结果也适用。
以下修改后的代码:
public class ExceptionsStatic {
public static void main(String... args)
{
withNormal();
withTry();
}
static int fooVar;
static void foo()
{
fooVar++;
}
static int foo2Var;
static void foo2() throws Exception
{
foo2Var++;
}
static void withTry()
{
long t1 = System.currentTimeMillis();
foo2Var = 0;
for(int i = 0; i < 100000000; i++)
{
try
{
foo2();
}
catch(Exception e)
{
}
}
long t2 = System.currentTimeMillis();
System.out.println("try time taken " + (t2 - t1) + "; " + foo2Var);
}
static void withNormal()
{
long t1 = System.currentTimeMillis();
fooVar = 0;
for(int i = 0; i < 100000000; i++)
{
foo();
}
long t2 = System.currentTimeMillis();
System.out.println("normal time taken " + (t2 - t1) + "; " + fooVar);
}
答案 2 :(得分:0)
这是一个微不足道的基准测试。使用1
或2
作为程序参数,使用-XX:+PrintCompilation -verbose:class -verbose:gc
作为JVM参数。
public class TryBlockBenchmark {
private static final int MEASUREMENTS = 100;
private static int dummy = 0;
public static void main(String[] args) {
boolean tryBlock = args[0].equals("1");
System.out.println(tryBlock ? "try block" : "no try block");
for (int i = 0; i < MEASUREMENTS; i++) {
long start = System.currentTimeMillis();
if (tryBlock) {
benchmarkTryBlock();
} else {
benchmarkNoTryBlock();
}
long end = System.currentTimeMillis();
System.out.println((end - start) + " ms");
}
System.out.println("(" + dummy + ")");
}
private static void benchmarkTryBlock() {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
try {
staticMethod();
} catch (Exception e) {
}
}
}
private static void benchmarkNoTryBlock() {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
staticMethod();
}
}
private static void staticMethod() {
dummy++;
}
}
在C2Q6600 @ 3GHz上使用Java 1.6.0_24 64位HotSpot Server VM,在第一次测量之后,两个版本的时间稳定在266 ms(+/- 1 ms)。手动内联staticMethod()
时的时间也相同,这可以从HotSpot预期。当HotSpot将其优化时,删除dummy++
行会将时间减少到0毫秒。
我还使用32位的Java 1.6.0_24进行了测试,并且HotSpot Server VM具有相同的结果,但HotSpot Client VM的两个版本产生的结果大约为8660毫秒(+/- 20毫秒)。
因此,我们可以得出结论,服务器VM具有比客户端VM更好的优化,并且没有做任何事情的try-catch要么被HotSpot优化掉,要么它不会影响性能。要查找它是什么,由HotSpot生成print the assembly code。
总的来说,衡量一无所获的事情是毫无意义的。