Theme Preview

Java中使用实例变量访问静态成员存在潜在的性能问题

由 李晓岚 在 2010年11月16日发表

在访问类静态成员时,有两种方式,一种是使用类名访问,另一种则是使用实例变量访问。通常我们认为这两种方式除了语法上的区别,其它的方面都相同。事实上,Java编译器在处理这两种情况时,进行了区别对待,使得通过不同方式访问静态成员时,存在轻微的性能方面的不同。

以下的结果基于javac 1.6.0_20编译器。

class Foo{
	private static int bar = 0;
	
	private static void bar(){
	}

	public static void via_class_name(){
		// 通过类名访问静态成员		
		int i = Foo.bar;
		Foo.bar();
	}

	public static void via_instance_reference( Foo foo ){
		// 通过实例变量访问静态成员
		int i = foo.bar;
		foo.bar();
	}
}

在Foo类中,函数via_class_name中通过类名的方式来访问静态成员,而在via_instance_reference函数中,则通过类的实例变量foo来访问静态成员。使用javac编译器编译上述类文件。然后使用Java 6附带的Java反编译工具javap(The Java Class File Disassembler)对编译生成的类文件进行反编译,得到如下反编译结果。

class Foo extends java.lang.Object{
Foo();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public static void via_class_name();
 Code:
   0:   getstatic       #2; //Field bar:I
   3:   istore_0
   4:   invokestatic    #3; //Method bar:()V
   7:   return

public static void via_instance_reference(Foo);
  Code:
   0:   aload_0             // 这两行的操作没有任何
   1:   pop                 // 实际作用
   2:   getstatic       #2; //Field bar:I
   5:   istore_1
   6:   aload_0             // 这两行的操作同样没有任何
   7:   pop                 // 实际作用
   8:   invokestatic    #3; //Method bar:()V
   11:  return

static {};
  Code:
   0:   iconst_0
   1:   putstatic       #2; //Field bar:I
   4:   return

}

对比via_class_name和via_instance_reference函数的字节码,可以发现,如果通过实例变量访问静态成员,编译器生成了无用的字节码(高亮的17,18,21,22共四行)。无用的字节码所做的操作为:将引用静态成员的实例变量压人栈(aload_0),随即又进行了弹栈操作(pop),没有产生任何的副作用,完全可以省略。

不过上述结果只是在Oracle(Sun)的Java SE6编译器下发生的,对于其它Java编译器没有做测试。同时也可以看到,编译器javac没有对字节码进行任何的优化。

所以,在引用静态成员时,最好还是使用类名来引用,除了不会产生语法上误导外,还能得到一定的性能改善。估计在实际运行时这点性能问题可能会被JIT优化掉。虽然有可能被优化掉,不存在任何的性能问题,但是这样的优化如果在编译字节码时进行,则要比JIT优化来得方便和安全得多。

 

标签:performance

comments powered by Disqus