一、关于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