Java 从字节码角度处理前端编译问题

原创
2021/03/07 10:30
阅读数 192

一、关于Java编译

java从代码编写到程序运行需要两次编译,第一次是Javac编译,第二次是JIT+解释器编译

  • Javac是前端编译:负责字节码优化
  • JIT+解释器是后端编译:负责字节码优化,存储空间优化,内联优化(inline)

前端编译优化问题是什么?

很多面试题都会问题类、构造方法、静态代码块、非静态代码块执行顺序,以及final和常量,synchornized,javac优化问题,这类问题实际上我们开发时也需要注意,毕竟出问题也一般不好定位。

解决这类问题的办法,实际上需要通过查看字节码完成,但字节码本质上是二进制,因此我们可以使用伪字节码工具,尽可能查看清楚的查看class文件,这里我们使用ASM ByteCode Outline 、当然也可以使用Java2Smali,实际上smali更加接近真实字节码,但该插件有个缺点会生成smali文件在java类目录下,因此需要你手动删除。

二、字节码查看工具

ASM ByteCode Outline

java source

 public static String getA(){
        String  str = "id_";
        str += "12345";
        str += "_end";
        return  str;
    }

    public static String getB(){
        StringBuilder sb = new StringBuilder();
        sb.append("id_")
                .append("123456")
                .append("_end");
        return  sb.toString();
    }

bytecode 

@groovyx.ast.bytecode.Bytecode
  public static String getA() {
    ldc "id_"
    astore 0
    _new 'java/lang/StringBuilder'
    dup
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    aload 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ldc "12345"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    astore 0
    _new 'java/lang/StringBuilder'
    dup
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    aload 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ldc "_end"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    astore 0
    aload 0
    areturn
  }

  @groovyx.ast.bytecode.Bytecode
  public static String getB() {
    _new 'java/lang/StringBuilder'
    dup
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    astore 0
    aload 0
    ldc "id_"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ldc "123456"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ldc "_end"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    pop
    aload 0
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    areturn
  }

从上面对比可知,

str 通过 "+" 会使用StringBuilder完成该操作,每个“+”都会创建StringBuilder对象

 

三、javac 常量问题

javac常量问题属于编译时常量池问题,java在编译时就会创建常量池,在Android 7.0 之后,字符串常量池移到对空间,字符串常量可以在运行时添加,因此JIT会做响应处理。

public class AppTest {

    private final  int C = 2;
    private int A = 1;

    private final String D = "D";

    private final String B = "B";
    private String B2 = "B";

    private String F = "F";
    private final String E = "E";
    private static final String AE = "AB"+"CDE";

    private static AppTest appTest = new AppTest();

    static {
        FGH = "GHF";
    }
    public static  String FGH = "FGH";

    static {
        FGH = "GFH";
    }
    {
        FGH = "HGF";
        mA = 2;
    }
    private int mA = 3;
    {
        mA = 1;
    }

    public void helloWorld(){
        int a = A + 3;
        int b = 1 + 3;
        int c = C + 3;
        int d = b + 4;

        String AB_1 = "AB";
        String AB_2 = "A" + "B";
        String AB_3 = "A" + B;
        String AB_4 = "A" + B2;

        String aA  = "A";
        String AB_5 = aA +"B";

        String ABD = new String("A"+"B" +D);

    }

    public static void main(String[] args) {
        AppTest appTest = new AppTest();

    }

}

那么我们从ASM角度和smali角度查看

ASM

// class version 51.0 (51)
// access flags 0x21
public class cntest/AppTest {

  // compiled from: AppTest.java

  // access flags 0x12
  private final I C = 2

  // access flags 0x2
  private I A

  // access flags 0x12
  private final Ljava/lang/String; D = "D"

  // access flags 0x12
  private final Ljava/lang/String; B = "B"

  // access flags 0x2
  private Ljava/lang/String; B2

  // access flags 0x2
  private Ljava/lang/String; F

  // access flags 0x12
  private final Ljava/lang/String; E = "E"

  // access flags 0x1A
  private final static Ljava/lang/String; AE = "ABCDE"

  // access flags 0xA
  private static Lcntest/AppTest; appTest

  // access flags 0x9
  public static Ljava/lang/String; FGH

  // access flags 0x2
  private I mA

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 5 L1
    ALOAD 0
    ICONST_2
    PUTFIELD cntest/AppTest.C : I
   L2
    LINENUMBER 6 L2
    ALOAD 0
    ICONST_1
    PUTFIELD cntest/AppTest.A : I
   L3
    LINENUMBER 8 L3
    ALOAD 0
    LDC "D"
    PUTFIELD cntest/AppTest.D : Ljava/lang/String;
   L4
    LINENUMBER 10 L4
    ALOAD 0
    LDC "B"
    PUTFIELD cntest/AppTest.B : Ljava/lang/String;
   L5
    LINENUMBER 11 L5
    ALOAD 0
    LDC "B"
    PUTFIELD cntest/AppTest.B2 : Ljava/lang/String;
   L6
    LINENUMBER 13 L6
    ALOAD 0
    LDC "F"
    PUTFIELD cntest/AppTest.F : Ljava/lang/String;
   L7
    LINENUMBER 14 L7
    ALOAD 0
    LDC "E"
    PUTFIELD cntest/AppTest.E : Ljava/lang/String;
   L8
    LINENUMBER 28 L8
    LDC "HGF"
    PUTSTATIC cntest/AppTest.FGH : Ljava/lang/String;
   L9
    LINENUMBER 29 L9
    ALOAD 0
    ICONST_2
    PUTFIELD cntest/AppTest.mA : I
   L10
    LINENUMBER 31 L10
    ALOAD 0
    ICONST_3
    PUTFIELD cntest/AppTest.mA : I
   L11
    LINENUMBER 33 L11
    ALOAD 0
    ICONST_1
    PUTFIELD cntest/AppTest.mA : I
   L12
    LINENUMBER 34 L12
    RETURN
   L13
    LOCALVARIABLE this Lcntest/AppTest; L0 L13 0
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x1
  public helloWorld()V
   L0
    LINENUMBER 37 L0
    ALOAD 0
    GETFIELD cntest/AppTest.A : I
    ICONST_3
    IADD
    ISTORE 1
   L1
    LINENUMBER 38 L1
    ICONST_4
    ISTORE 2
   L2
    LINENUMBER 39 L2
    ICONST_5
    ISTORE 3
   L3
    LINENUMBER 40 L3
    ILOAD 2
    ICONST_4
    IADD
    ISTORE 4
   L4
    LINENUMBER 42 L4
    LDC "AB"
    ASTORE 5
   L5
    LINENUMBER 43 L5
    LDC "AB"
    ASTORE 6
   L6
    LINENUMBER 44 L6
    LDC "AB"
    ASTORE 7
   L7
    LINENUMBER 45 L7
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "A"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    GETFIELD cntest/AppTest.B2 : Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 8
   L8
    LINENUMBER 47 L8
    LDC "A"
    ASTORE 9
   L9
    LINENUMBER 48 L9
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 9
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "B"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 10
   L10
    LINENUMBER 50 L10
    NEW java/lang/String
    DUP
    LDC "ABD"
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    ASTORE 11
   L11
    LINENUMBER 52 L11
    RETURN
   L12
    LOCALVARIABLE this Lcntest/AppTest; L0 L12 0
    LOCALVARIABLE a I L1 L12 1
    LOCALVARIABLE b I L2 L12 2
    LOCALVARIABLE c I L3 L12 3
    LOCALVARIABLE d I L4 L12 4
    LOCALVARIABLE AB_1 Ljava/lang/String; L5 L12 5
    LOCALVARIABLE AB_2 Ljava/lang/String; L6 L12 6
    LOCALVARIABLE AB_3 Ljava/lang/String; L7 L12 7
    LOCALVARIABLE AB_4 Ljava/lang/String; L8 L12 8
    LOCALVARIABLE aA Ljava/lang/String; L9 L12 9
    LOCALVARIABLE AB_5 Ljava/lang/String; L10 L12 10
    LOCALVARIABLE ABD Ljava/lang/String; L11 L12 11
    MAXSTACK = 3
    MAXLOCALS = 12

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 55 L0
    NEW cntest/AppTest
    DUP
    INVOKESPECIAL cntest/AppTest.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 57 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    LOCALVARIABLE appTest Lcntest/AppTest; L1 L2 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 17 L0
    NEW cntest/AppTest
    DUP
    INVOKESPECIAL cntest/AppTest.<init> ()V
    PUTSTATIC cntest/AppTest.appTest : Lcntest/AppTest;
   L1
    LINENUMBER 20 L1
    LDC "GHF"
    PUTSTATIC cntest/AppTest.FGH : Ljava/lang/String;
   L2
    LINENUMBER 22 L2
    LDC "FGH"
    PUTSTATIC cntest/AppTest.FGH : Ljava/lang/String;
   L3
    LINENUMBER 25 L3
    LDC "GFH"
    PUTSTATIC cntest/AppTest.FGH : Ljava/lang/String;
   L4
    LINENUMBER 26 L4
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0
}

Smali

.class public Lcntest/AppTest;
.super Ljava/lang/Object;
.source "AppTest.java"


# static fields
.field private static final AE:Ljava/lang/String; = "ABCDE"

.field public static FGH:Ljava/lang/String;

.field private static appTest:Lcntest/AppTest;


# instance fields
.field private A:I

.field private final B:Ljava/lang/String;

.field private B2:Ljava/lang/String;

.field private final C:I

.field private final D:Ljava/lang/String;

.field private final E:Ljava/lang/String;

.field private F:Ljava/lang/String;

.field private mA:I


# direct methods
.method static constructor <clinit>()V
    .registers 1

    .prologue
    .line 17
    new-instance v0, Lcntest/AppTest;

    invoke-direct {v0}, Lcntest/AppTest;-><init>()V

    sput-object v0, Lcntest/AppTest;->appTest:Lcntest/AppTest;

    .line 20
    const-string v0, "GHF"

    sput-object v0, Lcntest/AppTest;->FGH:Ljava/lang/String;

    .line 22
    const-string v0, "FGH"

    sput-object v0, Lcntest/AppTest;->FGH:Ljava/lang/String;

    .line 25
    const-string v0, "GFH"

    sput-object v0, Lcntest/AppTest;->FGH:Ljava/lang/String;

    .line 26
    return-void
.end method

.method public constructor <init>()V
    .registers 4

    .prologue
    const/4 v2, 0x2

    const/4 v1, 0x1

    .line 3
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 5
    iput v2, p0, Lcntest/AppTest;->C:I

    .line 6
    iput v1, p0, Lcntest/AppTest;->A:I

    .line 8
    const-string v0, "D"

    iput-object v0, p0, Lcntest/AppTest;->D:Ljava/lang/String;

    .line 10
    const-string v0, "B"

    iput-object v0, p0, Lcntest/AppTest;->B:Ljava/lang/String;

    .line 11
    const-string v0, "B"

    iput-object v0, p0, Lcntest/AppTest;->B2:Ljava/lang/String;

    .line 13
    const-string v0, "F"

    iput-object v0, p0, Lcntest/AppTest;->F:Ljava/lang/String;

    .line 14
    const-string v0, "E"

    iput-object v0, p0, Lcntest/AppTest;->E:Ljava/lang/String;

    .line 28
    const-string v0, "HGF"

    sput-object v0, Lcntest/AppTest;->FGH:Ljava/lang/String;

    .line 29
    iput v2, p0, Lcntest/AppTest;->mA:I

    .line 31
    const/4 v0, 0x3

    iput v0, p0, Lcntest/AppTest;->mA:I

    .line 33
    iput v1, p0, Lcntest/AppTest;->mA:I

    .line 34
    return-void
.end method

.method public static main([Ljava/lang/String;)V
    .registers 2
    .param p0, "args"    # [Ljava/lang/String;

    .prologue
    .line 55
    new-instance v0, Lcntest/AppTest;

    invoke-direct {v0}, Lcntest/AppTest;-><init>()V

    .line 57
    .local v0, "appTest":Lcntest/AppTest;
    return-void
.end method


# virtual methods
.method public helloWorld()V
    .registers 14

    .prologue
    .line 37
    iget v11, p0, Lcntest/AppTest;->A:I

    add-int/lit8 v6, v11, 0x3

    .line 38
    .local v6, "a":I
    const/4 v8, 0x4

    .line 39
    .local v8, "b":I
    const/4 v9, 0x5

    .line 40
    .local v9, "c":I
    add-int/lit8 v10, v8, 0x4

    .line 42
    .local v10, "d":I
    const-string v1, "AB"

    .line 43
    .local v1, "AB_1":Ljava/lang/String;
    const-string v2, "AB"

    .line 44
    .local v2, "AB_2":Ljava/lang/String;
    const-string v3, "AB"

    .line 45
    .local v3, "AB_3":Ljava/lang/String;
    new-instance v11, Ljava/lang/StringBuilder;

    invoke-direct {v11}, Ljava/lang/StringBuilder;-><init>()V

    const-string v12, "A"

    invoke-virtual {v11, v12}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v11

    iget-object v12, p0, Lcntest/AppTest;->B2:Ljava/lang/String;

    invoke-virtual {v11, v12}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v11

    invoke-virtual {v11}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v4

    .line 47
    .local v4, "AB_4":Ljava/lang/String;
    const-string v7, "A"

    .line 48
    .local v7, "aA":Ljava/lang/String;
    new-instance v11, Ljava/lang/StringBuilder;

    invoke-direct {v11}, Ljava/lang/StringBuilder;-><init>()V

    invoke-virtual {v11, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v11

    const-string v12, "B"

    invoke-virtual {v11, v12}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v11

    invoke-virtual {v11}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v5

    .line 50
    .local v5, "AB_5":Ljava/lang/String;
    new-instance v0, Ljava/lang/String;

    const-string v11, "ABD"

    invoke-direct {v0, v11}, Ljava/lang/String;-><init>(Ljava/lang/String;)V

    .line 52
    .local v0, "ABD":Ljava/lang/String;
    return-void
.end method

从上面的伪字节码我们可以还原javac编译之后的代码

public class AppTest {

    private static final String AE = "ABCDE"; //javac 优化,指向常量
    private static AppTest appTest;
    public static  String FGH;

    private final int C;
    private int A;
    private final String D;
    private final String B;
    private String B2;
    private String F;
    private final String E;
    private int mA;

    public static  class_init(){  
     //类初始化函数,由ClassLoader去初始化
        appTest = new AppTest();
        FGH = "GHF"; //指向常量
        FGH = "FGH"; //指向常量
        FGH = "GFH"; //指向常量
    }

    public  AppTest(){
        super();
        //下面语句会在super之后执行
        {
            C = 2; //指向常量
            A = 1; //指向常量
            D = "D";//指向常量
            B = "B";//指向常量
            B2 = "B";//指向常量
            F = "F";//指向常量
            E = "E";//指向常量
            FGH = "HGF";//指向常量
            mA = 2;//指向常量
            mA = 3;//指向常量
            mA = 1;//指向常量
        }

        //后面是构造方法其他代码,因为类外赋值的的变量在编译后会优先初始化
    }

    public void helloWorld(){
        int a = A + 3; //a最终指向栈内存
        int b = 4; //javac optimized 自动计算
        int c = 5; //javac optimized 自动计算
        int d = b + 4; //虽然指向常量,但javac并没有优化,原因待解,这里d最终指向栈内存,因为d属于基本类型

        String AB_1 = "AB"; //javac optimized  AB是常量,常量吃不存在,添加到常量池
        String AB_2 = "AB"; //javac optimized  javac 自动合并,合并后从常量池查找,复制
        String AB_3 = "A" + B; //javac optimized  javac 自动合并,合并后从常量池查找,复制
        String AB_4 = "A" + B2;  //javac 不会处理,AB_4 最终指向堆空间

        String aA  = "A"; //生成常量A,添加到常量池,并赋值给aA
        String AB_5 = aA +"B"; //当前aA是局部变量引用常量,明显可以可以继续优化,但并没有,待解,此处AB_5最终指向堆空间

        String ABD = new String("A"+"B" +D); //javac 自动合并 ABD,然后new String,导致ABD最终指向堆空间

    }

    public static void main(String[] args) {
        AppTest appTest = new AppTest();

    }

}

三、运行时常量问题

java常量池中,存在运行时常量池

主要分为:string.intern() , 字节小于1Byte的包装类型。

     Integer ia = 200;
        Integer ib = 200;

        System.out.println(ia==ib);  //false

        Integer ic = 100;
        Integer id = 100;

        System.out.println(ic==id); //true


        final String A = "A";
        String ABC = A + "BC"  ;
        System.out.println(ABC=="ABC");

        String c = new String("c") +new String("d");
        System.out.println(c == c.intern()); //true

        String s = new String("a") + new String("b");
        System.out.println(s == s.intern()); //t

编译后的代码

// class version 51.0 (51)
// access flags 0x21
public class io/rmiri/keepalive/TestJavac {


  @groovyx.ast.bytecode.Bytecode
  public void <init>() {
    aload 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    return
  }

  @groovyx.ast.bytecode.Bytecode
  public static void main(String[] a) {
    sipush 200
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    astore 1
    sipush 200
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    astore 2
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 1
    aload 2
    if_acmpne l0
    iconst_1
    _goto l1
   l0
    iconst_0
   l1
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
    bipush 100
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    astore 3
    bipush 100
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    astore 4
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 3
    aload 4
    if_acmpne l2
    iconst_1
    _goto l3
   l2
    iconst_0
   l3
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
    ldc "A"
    astore 5
    ldc "ABC"
    astore 6
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 6
    ldc "ABC"
    if_acmpne l4
    iconst_1
    _goto l5
   l4
    iconst_0
   l5
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
    _new 'java/lang/StringBuilder'
    dup
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    _new 'java/lang/String'
    dup
    ldc "1"
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    _new 'java/lang/String'
    dup
    ldc "2"
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    astore 7
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 7
    aload 7
    INVOKEVIRTUAL java/lang/String.intern ()Ljava/lang/String;
    if_acmpne l6
    iconst_1
    _goto l7
   l6
    iconst_0
   l7
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
    _new 'java/lang/StringBuilder'
    dup
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    _new 'java/lang/String'
    dup
    ldc "a"
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    _new 'java/lang/String'
    dup
    ldc "b"
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    astore 8
    getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
    aload 8
    aload 8
    INVOKEVIRTUAL java/lang/String.intern ()Ljava/lang/String;
    if_acmpne l8
    iconst_1
    _goto l9
   l8
    iconst_0
   l9
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
    return
  }
}

 

其中对于intern用法这里必须强调一下

java 7中string常量在堆空间,因此如果是如下合成的变量本身是存在堆空间中,但是intern调用之后,会将“cd”引用添加至常量池中,因此,就有可能出现局部变量和常量相等的情况

String c = new String("c") +new String("d");

 

包装类型,我们看到字节码中

Interge.valueOf(),该方法对 -128到127之间的数据有特殊处理

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

 

四、异常问题

异常捕获代码会让java 字节码增多,此外,还有一点是try{}catch中的代码可能不被javac优化

 // 遍历arr数组: 通过异常的方式
    private static void endByException(int[] arr) {
        try {
            int i=0;
            while (true) {
                arr[i]=0;
                i++;
            }
        } catch (IndexOutOfBoundsException e) {
        }
    }

    // 遍历arr数组: 通过边界的方式
    private static void endByRange(int[] arr) {
        for (int i=0; i<arr.length; i++) {
            arr[i]=0;
            i++;
        }
    }

编译后的代码

 // access flags 0xA
  private static endByException([I)V
    TRYCATCHBLOCK L0 L1 L1 java/lang/IndexOutOfBoundsException
   L0
    LINENUMBER 26 L0
    ICONST_0
    ISTORE 1
   L2
    LINENUMBER 28 L2
   FRAME APPEND [I]
    ALOAD 0
    ILOAD 1
    ICONST_0
    IASTORE
   L3
    LINENUMBER 29 L3
    IINC 1 1
    GOTO L2
   L1
    LINENUMBER 31 L1
   FRAME FULL [[I] [java/lang/IndexOutOfBoundsException]
    ASTORE 1
   L4
    LINENUMBER 33 L4
    RETURN
   L5
    LOCALVARIABLE i I L2 L1 1
    LOCALVARIABLE arr [I L0 L5 0
    MAXSTACK = 3
    MAXLOCALS = 2

  // access flags 0xA
  private static endByRange([I)V
   L0
    LINENUMBER 37 L0
    ICONST_0
    ISTORE 1
   L1
   FRAME APPEND [I]
    ILOAD 1
    ALOAD 0
    ARRAYLENGTH
    IF_ICMPGE L2
   L3
    LINENUMBER 38 L3
    ALOAD 0
    ILOAD 1
    ICONST_0
    IASTORE
   L4
    LINENUMBER 39 L4
    IINC 1 1
   L5
    LINENUMBER 37 L5
    IINC 1 1
    GOTO L1
   L2
    LINENUMBER 41 L2
   FRAME CHOP 1
    RETURN
   L6
    LOCALVARIABLE i I L1 L2 1
    LOCALVARIABLE arr [I L0 L6 0
    MAXSTACK = 3
    MAXLOCALS = 2

可以看到,没有异常的处理的代码中有个 IF_ICMPGE,该指令是优先对比要赋给数组的值和新值是否相同,相同的化才会写入

运行效率

endByRange time:1ms
endByException time:37ms

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部