[讨论] 静态方法调用疑问
haoweishow
2011-09-19
源码: /** * javac TestStaticMethod.java * javap -verbose TestStaticMethod * @author EX-HAOWEI002 * */ public class TestStaticMethod { public static void foo(){ } public void call1(){ foo(); } public void call2(){ TestStaticMethod.foo(); } public void call3(){ this.foo(); } }
执行命令: javap -verbose TestStaticMethod 显示结果如下: D:\ws\JVM\src>javap -verbose TestStaticMethod Compiled from "TestStaticMethod.java" public class TestStaticMethod extends java.lang.Object SourceFile: "TestStaticMethod.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #4.#15; // java/lang/Object."<init>":()V const #2 = Method #3.#16; // TestStaticMethod.foo:()V const #3 = class #17; // TestStaticMethod const #4 = class #18; // java/lang/Object const #5 = Asciz <init>; const #6 = Asciz ()V; const #7 = Asciz Code; const #8 = Asciz LineNumberTable; const #9 = Asciz foo; const #10 = Asciz call1; const #11 = Asciz call2; const #12 = Asciz call3; const #13 = Asciz SourceFile; const #14 = Asciz TestStaticMethod.java; const #15 = NameAndType #5:#6;// "<init>":()V const #16 = NameAndType #9:#6;// foo:()V const #17 = Asciz TestStaticMethod; const #18 = Asciz java/lang/Object; { public TestStaticMethod(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 public static void foo(); Code: Stack=0, Locals=0, Args_size=0 0: return LineNumberTable: line 11: 0 public void call1(); Code: Stack=0, Locals=1, Args_size=1 0: invokestatic #2; //Method foo:()V 3: return LineNumberTable: line 14: 0 line 15: 3 public void call2(); Code: Stack=0, Locals=1, Args_size=1 0: invokestatic #2; //Method foo:()V 3: return LineNumberTable: line 18: 0 line 19: 3 public void call3(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: pop 2: invokestatic #2; //Method foo:()V 5: return LineNumberTable: line 22: 0 line 23: 5 }
call1和call2是一样的,call3里用了this,为啥就不一样了呢? 我的理解是:不管是通过this,还是类名访问静态方法foo,因为在内存中都是同一个foo,应该是一样的。 还有就是根据字节码,是否能够断定,用this访问静态方法,多了两个指令操作,因此效率比其他两个低呢?
还请各位大大,帮忙解释一下。 |
|
RednaxelaFX
2011-09-19
没啥不一样的。aload_0把this压到操作数栈顶,紧接着pop就把它弹出去了。
生成这样的代码只是因为javac比较懒没有分析一些明显可以优化的点而已。实际生成的call1、call2、call3的语义是一样的。 字节码的差异不能用来断定程序效率的高低。字节码一样可以推断性能在同等水平,但字节码不一样说明不了性能问题。 在HotSpot VM的默认执行模式里的话,这仨方法被解释执行时call1与call2会比call3快一些,因为它的解释器没做什么优化;但被JIT编译的话三者应该得到一样的结果,多出来的两条指令在JIT编译的最初阶段就已经被干掉了。 唯一细微的差别是,call3的字节码指令更长,会占用更多的内联预算——也就是说如果call3在同一个方法内被多次内联的话,能被内联的次数会少些。不考虑这种边角情况的话,三者是一样的。 |
|
haoweishow
2011-09-19
谢谢你的回复,又学习啦。
![]() |
|
googya
2011-09-19
看到实例对象调用静态方法,我刚开始看着很不习惯呢。。。我想知道为什么这样做是没有问题的???
在ruby中就会出问题的。。c#中貌似也会。。Java怎么就。。。。。 真是很困惑。 |
|
RednaxelaFX
2011-09-20
googya 写道 看到实例对象调用静态方法,我刚开始看着很不习惯呢。。。我想知道为什么这样做是没有问题的???
在ruby中就会出问题的。。c#中貌似也会。。Java怎么就。。。。。 真是很困惑。 你的感觉没错。Java的这种设计就是挖坑让人容易掉进去。 例如说这样: Thread t = new Thread(new Runnable() { // ... }); t.start(); t.sleep(5000); // 注意这里 // ... 这样的话,代码看起来像是让t睡眠了5秒,实际上却是让当前线程睡眠了5秒,因为sleep是个静态方法。超级坑爹。 |