[讨论] try catch放循环体内还是外?

richard_2010 2011-08-05

有这样的一段代码:

 

public void test1() {
		while(true) {
			try {
				Thread.sleep(30*60*1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void test2() {
		try {
			while(true) {
				Thread.sleep(30*60*1000);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
 

其字节码为:

 

public void test1();
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   ldc2_w  #4; //long 1800000l
   3:   invokestatic    #6; //Method java/lang/Thread.sleep:(J)V
   6:   goto    0
   9:   astore_1
   10:  aload_1
   11:  invokevirtual   #8; //Method java/lang/InterruptedException.printStackTr
ace:()V
   14:  goto    0
  Exception table:
   from   to  target type
     0     6     9   Class java/lang/InterruptedException

  LineNumberTable:
   line 10: 0
   line 14: 6
   line 11: 9
   line 13: 10
   line 14: 14

  StackMapTable: number_of_entries = 2
   frame_type = 0 /* same */
   frame_type = 72 /* same_locals_1_stack_item */
     stack = [ class java/lang/InterruptedException ]


public void test2();
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   ldc2_w  #4; //long 1800000l
   3:   invokestatic    #6; //Method java/lang/Thread.sleep:(J)V
   6:   goto    0
   9:   astore_1
   10:  aload_1
   11:  invokevirtual   #8; //Method java/lang/InterruptedException.printStackTr
ace:()V
   14:  return
  Exception table:
   from   to  target type
     0     9     9   Class java/lang/InterruptedException

  LineNumberTable:
   line 21: 0
   line 23: 9
   line 25: 10
   line 27: 14

  StackMapTable: number_of_entries = 2
   frame_type = 0 /* same */
   frame_type = 72 /* same_locals_1_stack_item */
     stack = [ class java/lang/InterruptedException ]
 可以看到,除了test1()里面发生异常后是继续执行循环 goto 0, test2()里面发生异常是结束 return以外,这里是逻辑上的不同,别的字节码并太大差异,可不可以认为try catch放循环体内和外并无效率上的差别呢?
richard_2010 2011-08-05
顺便问个问题,命令行javac,java后面加参数-server报“无法定位程序输入点_JVM_LoadSystemLibrary@4 于动态链接库 jvm.dll 上。” 是肿么个情况?
RednaxelaFX 2011-08-05
richard_2010 写道
顺便问个问题,命令行javac,java后面加参数-server报“无法定位程序输入点_JVM_LoadSystemLibrary@4 于动态链接库 jvm.dll 上。” 是肿么个情况?

怎么会报这么奇怪的错误的。你确定你用的“java”是JDK/bin里的而不是JRE/bin里的么?
richard_2010 2011-08-05
RednaxelaFX 写道

怎么会报这么奇怪的错误的。你确定你用的“java”是JDK/bin里的而不是JRE/bin里的么?


JAVA_HOME是jdk的安装目录,这个问题没遇到过就先无视吧,还是帮忙看看trycatch吧
kakueiken 2011-08-05
trycatch
就效率而言,始终应该放在外循环。
chengjf0526 2011-08-05
很久很久之前,有人开始讨论这个问题。
很久很久之后的今天,依然有人讨论这个问题。
为何大家总要关心所谓的效率,而不关心业务?
两种方式所实现的功能完全不同,谈何效率对比?
这样的事儿就不要再纠结了,主要是看你想要怎样的效果。
kakueiken 2011-08-05
因为曾经刚出道时,写在内循环了。
被一个资深的头给骂了,写出这么垃圾的代码。。。
RednaxelaFX 2011-08-05
chengjf0526 写道
很久很久之前,有人开始讨论这个问题。
很久很久之后的今天,依然有人讨论这个问题。
为何大家总要关心所谓的效率,而不关心业务?
两种方式所实现的功能完全不同,谈何效率对比?
这样的事儿就不要再纠结了,主要是看你想要怎样的效果。

只关心业务的可以在其它圈子讨论…
关心业务跟关心技术本来不互斥。当然自己心里把它们看成互斥的概念的自由是每个人都可以有的
RednaxelaFX 2011-08-05
首先,语义不同的东西是不适合用来比较性能的。有时候是迫于逻辑需要你不得不把try/catch放在循环内,这个时候就算你知道放在循环外比较快也没办法——除非做更大的代码变更来避开把try/catch放在循环内的需求。

其次,简单来说字节码是看不出性能问题的。如果两个方法的Code属性,包括其关联的属性表(包括ExceptionTable)一模一样,那性能应该也是一样的。但只要不是一模一样,差异点到底有多少性能差异就说不好了。

回到主题。try/catch/finally会给代码的控制流图增加一些隐藏的控制流边,使编译器做优化麻烦一些。不过到底放里面还是外面对性能也没有影响要看情况。

针对顶楼的例子来看,让JDK6u23的client VM来跑,test1()在临生成机器码前的控制流图是这个样子的:


test2()的则是这个样子的:


控制流图确实看起来不一样对吧。图里红色的边是回边(从后向前跳的控制流边),黑边是普通的顺序直落的控制流边,蓝边是异常控制流边,蓝色虚线框是异常处理的控制范围。

不过实际执行循环的代码都是一样的:
test1()
  0x00c531ff: movl   $0x0,0x4(%esp)
  0x00c53207: movl   $0x1b7740,(%esp)   ;*invokestatic sleep
                                        ; - TestC1CFG::test1@3 (line 5)
  0x00c5320e: nop    
  0x00c5320f: call   0x00b7b450         ; OopMap{off=116}
                                        ;*invokestatic sleep
                                        ; - TestC1CFG::test1@3 (line 5)
                                        ;   {static_call}
  0x00c53214: nop                       ; OopMap{off=117}
                                        ;*goto
                                        ; - TestC1CFG::test1@6 (line 9)
  0x00c53215: test   %eax,0x970100      ;   {poll}
  0x00c5321b: jmp    0x00c531ff

  0x00c540bc: movl   $0x0,0x4(%esp)
  0x00c540c4: movl   $0x1b7740,(%esp)   ;*invokestatic sleep
                                        ; - TestC1CFG::test2@3 (line 16)
  0x00c540cb: call   0x00b7b450         ; OopMap{off=48}
                                        ;*invokestatic sleep
                                        ; - TestC1CFG::test2@3 (line 16)
                                        ;   {static_call}
  0x00c540d0: nop                       ; OopMap{off=49}
                                        ;*goto
                                        ; - TestC1CFG::test2@6 (line 16)
  0x00c540d1: test   %eax,0x970100      ;   {poll}
  0x00c540d7: jmp    0x00c540bc         ;*goto
                                        ; - TestC1CFG::test2@6 (line 16)

所以在这里例子里两个版本在不抛异常的时候在循环里的性能会是一样的。

=============================

我用来跑的代码是这样的:
public class TestC1CFG {
  public void test1() {
    while (true) {
      try {
        Thread.sleep(30*60*1000);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }
  
  public void test2() {
    try {
      while (true) {
        Thread.sleep(30*60*1000);
      }
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args) throws Exception {
    new InterruptedException(); // force class load
    
    Runnable task1 = new Runnable() {
      public void run() {
        new TestC1CFG().test1();
      }
    };
    
    Runnable task2 = new Runnable() {
      public void run() {
        new TestC1CFG().test2();
      }
    };
    
    Thread t1 = new Thread(task1);
    Thread t2 = new Thread(task2);
    t1.start();
    t2.start();
    
    Thread t3 = new Thread(task1);
    Thread t4 = new Thread(task2);
    t3.start();
    t4.start();
    
    Thread.sleep(2000);
    System.in.read();
  }
}

VM参数文件是:
+PrintCompilation
#+PrintCFGToFile
+PrintAssembly
CompileThreshold=2
richard_2010 2011-08-05
有文字有图,不错。
跟我想的差不多
话说怎么弄成汇编码来看?
Global site tag (gtag.js) - Google Analytics