欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-11 15:15:40
...

      先看一段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内存模型。


从二进制指令看包装类Integer在编译期处理优化及二进制指令代码解析
            
    
    博客分类: 学习笔记 Integerjava编译处理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

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

     

  • 从二进制指令看包装类Integer在编译期处理优化及二进制指令代码解析
            
    
    博客分类: 学习笔记 Integerjava编译处理jvm内存模型二进制指令 
  • 大小: 62.4 KB