从二进制指令看包装类Integer在编译期处理优化及二进制指令代码解析

      先看一段demo:

package com.onlyou;

/**
 * Created by cd_huang on 2017/5/25.
 */
public class Test {
	public static void main(String args[]) {
		intTest();
	}
	public static String intTest(){
		int aa =100;
		Integer bb =100;
		System.out.println(aa==bb);
		Integer cc =100;
		System.out.println(bb==cc);
		int dd =1000;
		Integer ee =1000;
		System.out.println(dd==ee);
		Integer ff =1000;
		System.out.println(ee==ff);
		Integer gg =null;
		System.out.println(bb==gg);
		System.out.println(aa==gg);
		return null;
	}
}

然后看运行结果:

true
true
true
false
false
Exception in thread "main" java.lang.NullPointerException
	at com.onlyou.Test.intTest(Test.java:23)
	at com.onlyou.Test.main(Test.java:8)

 第一个对比是int和Integer的对比,在编译期会处理成int100和Integer.intVal

ue获得int100,两个int100的对比,所以是true。

 第二个对比是Integer和Integer的对比,所以纯粹是两个对象,如果指向同一个内存位置就会为true,这里理论上是false。但是编译期做了优化,Integer bb =100,其实等同于Integer bb =Integer.valueOf(100),这两种写法编译后的字节码是一样的。Integer.valueOf方法代码如下:

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

 代码对-127到127之间的Integer对象做了缓存,所以这里会是同一个对象,所以是true。

第三个对比是int和Integer的对比,是true的原因同第一个对比。

第四个对比是Integer和Integer的对比,是false的原因是1000不在-127到127之间,没有缓存,所以是两个对象。

第五个对比是Integer和Integer的对比,Integer100和null肯定是false。

第六个对比是int和Integer的对比,在编译期会处理成int和Integer.intVal来进行两个int的对比,Integer为空,所以这里直接报空指针异常。

   所以,上面那段代码其实相当于下面这段代码。

package com.onlyou;

/**
 * Created by cd_huang on 2017/5/25.
 */
public class Test {
	public static void main(String args[]) {
		intTest();
	}
	public static String intTest(){
		int aa =100;
		Integer bb =Integer.valueOf(100);
		System.out.println(aa==bb.intValue());
		Integer cc =Integer.valueOf(100);
		System.out.println(bb==cc);
		int dd =1000;
		Integer ee =Integer.valueOf(1000);
		System.out.println(dd==ee.intValue());
		Integer ff =Integer.valueOf(1000);
		System.out.println(ee==ff);
		Integer gg =null;
		System.out.println(bb==gg);
		System.out.println(aa==gg.intValue());
		return null;
	}
}

   解释完了后我们来看编译后的指令是什么样子的。

C:\Users\hcd>javac c:\Test.java
C:\Users\hcd>javap -v c:\Test.class
Classfile /c:/Test.class
  Last modified 2017-5-25; size 1037 bytes
  MD5 checksum c36ad2112f8e55c60615146d0a116c8d
  Compiled from "Test.java"
public class com.onlyou.Test
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#22         //  java/lang/Object."<init>":()V
   #2 = Methodref          #7.#23         //  com/onlyou/Test.intTest:()Ljava/la
ng/String;
   #3 = Methodref          #24.#25        //  java/lang/Integer.valueOf:(I)Ljava
/lang/Integer;
   #4 = Fieldref           #26.#27        //  java/lang/System.out:Ljava/io/Prin
tStream;
   #5 = Methodref          #24.#28        //  java/lang/Integer.intValue:()I
   #6 = Methodref          #29.#30        //  java/io/PrintStream.println:(Z)V
   #7 = Class              #31            //  com/onlyou/Test
   #8 = Class              #32            //  java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               intTest
  #16 = Utf8               ()Ljava/lang/String;
  #17 = Utf8               StackMapTable
  #18 = Class              #33            //  java/lang/Integer
  #19 = Class              #34            //  java/io/PrintStream
  #20 = Utf8               SourceFile
  #21 = Utf8               Test.java
  #22 = NameAndType        #9:#10         //  "<init>":()V
  #23 = NameAndType        #15:#16        //  intTest:()Ljava/lang/String;
  #24 = Class              #33            //  java/lang/Integer
  #25 = NameAndType        #35:#36        //  valueOf:(I)Ljava/lang/Integer;
  #26 = Class              #37            //  java/lang/System
  #27 = NameAndType        #38:#39        //  out:Ljava/io/PrintStream;
  #28 = NameAndType        #40:#41        //  intValue:()I
  #29 = Class              #34            //  java/io/PrintStream
  #30 = NameAndType        #42:#43        //  println:(Z)V
  #31 = Utf8               com/onlyou/Test
  #32 = Utf8               java/lang/Object
  #33 = Utf8               java/lang/Integer
  #34 = Utf8               java/io/PrintStream
  #35 = Utf8               valueOf
  #36 = Utf8               (I)Ljava/lang/Integer;
  #37 = Utf8               java/lang/System
  #38 = Utf8               out
  #39 = Utf8               Ljava/io/PrintStream;
  #40 = Utf8               intValue
  #41 = Utf8               ()I
  #42 = Utf8               println
  #43 = Utf8               (Z)V
{
  public com.onlyou.Test();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>
":()V
         4: return
      LineNumberTable:
        line 6: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: invokestatic  #2                  // Method intTest:()Ljava/lang/Str
ing;
         3: pop
         4: return
      LineNumberTable:
        line 8: 0
        line 9: 4

  public static java.lang.String intTest();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=7, args_size=0
         0: bipush        100
         2: istore_0
         3: bipush        100
         5: invokestatic  #3                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
         8: astore_1
         9: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        12: iload_0
        13: aload_1
        14: invokevirtual #5                  // Method java/lang/Integer.intVal
ue:()I
        17: if_icmpne     24
        20: iconst_1
        21: goto          25
        24: iconst_0
        25: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
        28: bipush        100
        30: invokestatic  #3                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        33: astore_2
        34: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        37: aload_1
        38: aload_2
        39: if_acmpne     46
        42: iconst_1
        43: goto          47
        46: iconst_0
        47: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
        50: sipush        1000
        53: istore_3
        54: sipush        1000
        57: invokestatic  #3                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        60: astore        4
        62: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        65: iload_3
        66: aload         4
        68: invokevirtual #5                  // Method java/lang/Integer.intVal
ue:()I
        71: if_icmpne     78
        74: iconst_1
        75: goto          79
        78: iconst_0
        79: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
        82: sipush        1000
        85: invokestatic  #3                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        88: astore        5
        90: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        93: aload         4
        95: aload         5
        97: if_acmpne     104
       100: iconst_1
       101: goto          105
       104: iconst_0
       105: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
       108: aconst_null
       109: astore        6
       111: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
       114: aload_1
       115: aload         6
       117: if_acmpne     124
       120: iconst_1
       121: goto          125
       124: iconst_0
       125: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
       128: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
       131: iload_0
       132: aload         6
       134: invokevirtual #5                  // Method java/lang/Integer.intVal
ue:()I
       137: if_icmpne     144
       140: iconst_1
       141: goto          145
       144: iconst_0
       145: invokevirtual #6                  // Method java/io/PrintStream.prin
tln:(Z)V
       148: aconst_null
       149: areturn
      LineNumberTable:
        line 11: 0
        line 12: 3
        line 13: 9
        line 14: 28
        line 15: 34
        line 16: 50
        line 17: 54
        line 18: 62
        line 19: 82
        line 20: 90
        line 21: 108
        line 22: 111
        line 23: 128
        line 24: 148
      StackMapTable: number_of_entries = 12
           frame_type = 255 /* full_frame */
          offset_delta = 24
          locals = [ int, class java/lang/Integer ]
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]
           frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ int, class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]
           frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer ]
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]
           frame_type = 255 /* full_frame */
          offset_delta = 24
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]
           frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]
           frame_type = 82 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
           frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
 class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
          stack = [ class java/io/PrintStream, int ]

}

 上面就是intTest()方法编译后的二进制指令。这边简单的解释下二进制指令怎么看。

      首先简单介绍下jvm内存模型。


     没花太多时间找到更合适的图片了。在我的理解里,更希望以线程私有还是线程共享来区分上图的每个部分,感觉会更清楚。上图转自http://blog.csdn.net/u012152619/article/details/46968883,对内存模型没什么了解的可以先看链接里的内容,当然,最好是看一下周志明老师的《深入理解Java虚拟机》,最经典的虚拟机学习书本。上图中方法区和堆是线程共享的,即每个线程都可以访问,所以存在线程安全的问题。而虚拟机栈,本地方法栈,程序计数器是线程独有的。程序的执行,调用一个方法时,就相当于把方法进栈,在方法执行结束后出栈,符合后进先出。

     因为二进制指令主要是描述java代码的执行,所以主要关注虚拟机栈内的局部变量表和操作数栈。操作数栈是执行的真正工作区,是重中之重。二进制指令可以对照http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html查看含义。注意每次操作数栈的内容使用完后都会出栈。下面是我给二进制指令加了些注释,以及写了在每一条指令执行完后,操作数栈和本地变量表的变化。

public static java.lang.String intTest();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=7, args_size=0(操作数栈长度为3,本地常量表的长度为7,因为是静态方法,所以方法的参数个位为0)
         0: bipush        100 //valuebyte值带符号扩展成int值入栈。
(此时操作数栈内容为:100,null,null,局部变量表为:null,null,null,null,null,null,null)
         2: istore_0          //将栈顶int类型值保存到局部变量0中。
(此时操作数栈内容为:null,null,null,局部变量表为:100,null,null,null,null,null,null)
         3: bipush        100 //valuebyte值带符号扩展成int值入栈。
(此时操作数栈内容为:100,null,null,局部变量表为:100,null,null,null,null,null,null)
         5: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
	//调用静态方法Integer.valueOf
(此时操作数栈内容为:Integer(100)的引用,null,null,局部变量表为:100,null,null,null,null,null,null)
         8: astore_1          //将栈顶引用类型值保存到局部变量1中。
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
         9: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
(此时操作数栈内容为:PrintStream的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)   
        12: iload_0           //从局部变量0中装载int类型值入栈。
(此时操作数栈内容为:100,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        13: aload_1           //从局部变量1中装载引用类型值入栈。
(此时操作数栈内容为:Integer(100)的引用,100,PrintStream的引用,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        14: invokevirtual #9                  // Method java/lang/Integer.intVal
ue:()I   
(此时操作数栈内容为:100,100,PrintStream的引用,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        17: if_icmpne     24  //若栈顶两int类型值不相等则跳转。(跳转到24,即'24: iconst_0'这行)
(此时操作数栈内容为:PrintStream的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        20: iconst_1          //1(int)值入栈。
(此时操作数栈内容为:1,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)     
        21: goto          25  //无条件跳转到指定位置。(因为执行了20: iconst_1,所以跳过了24: iconst_0,直接跳到25: invokevirtual)
        24: iconst_0          //0(int)值入栈。
(此时操作数栈内容为:0,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        25: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V 
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null) 
        28: bipush        100
(此时操作数栈内容为:100,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        30: invokestatic  #8                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
(此时操作数栈内容为:Integer(100)的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
        33: astore_2
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,Integer(100)的引用,null,null,null,null)
        34: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
(逻辑同上面。。这里就不再重述了)
        37: aload_1
        38: aload_2
        39: if_acmpne     46
        42: iconst_1
        43: goto          47
        46: iconst_0
        47: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V
        50: sipush        1000
        53: istore_3
        54: sipush        1000
        57: invokestatic  #8                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        60: astore        4
        62: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        65: iload_3
        66: aload         4
        68: invokevirtual #9                  // Method java/lang/Integer.intVal
ue:()I
        71: if_icmpne     78
        74: iconst_1
        75: goto          79
        78: iconst_0
        79: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V
        82: sipush        1000
        85: invokestatic  #8                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        88: astore        5
        90: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        93: aload         4
        95: aload         5
        97: if_acmpne     104
       100: iconst_1
       101: goto          105
       104: iconst_0
       105: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V
       108: aconst_null
       109: astore        6
       111: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
       114: aload_1
       115: aload         6
       117: if_acmpne     124
       120: iconst_1
       121: goto          125
       124: iconst_0
       125: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V
       128: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
       131: iload_0
       132: aload         6
       134: invokevirtual #9                  // Method java/lang/Integer.intVal
ue:()I
       137: if_icmpne     144
       140: iconst_1
       141: goto          145
       144: iconst_0
       145: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Z)V
       148: aconst_null
       149: areturn

      文章中很多内容都只是个人的一些理解,如果有什么问题可以在评论里留言。欢迎交流。

     

猜你喜欢

转载自xiangshouxiyang.iteye.com/blog/2376374