JVM字節碼

1、jvm字節碼指令與javap
        將class文件編譯爲字節碼文件:javap -verbose ClassName.class > ClassName.txt
2、描述符(Descriptors)

Java字節碼 類型 描述
B byte 單字節
C char Unicode字符
D double 雙精度浮點數
F float 單精度浮點數
I int 整形
J long 長整形
L 引用 ClassName類型的實例
S short 短整形
Z boolean 布爾類型
[ 引用 一維數組

例如

java代碼 Java字節碼錶示
double d[][][] [[[D
Object myMethod(int i, double d, Thread t) myMethod(I, D, Ljava/lang/Thread;)Ljava/lang/Object;

 3、jvm是基於棧的架構

public class Test1{
    public static void main(String[] args){
        int a = 2;
        int b = 3;
        int c = a + b;
        System.out.println(c);
    }
}
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
        # stack=2:操作數棧的深度爲2
        # locals=4:本地變量表最大長度爲4(單位slot),64位是2,其他是1,索引從0開始,如果是非static方法索引0代表this
        # args_size=1:入參,1個參數,實例方法多一個this參數
    stack=2, locals=4, args_size=1
        # 常量2入棧
    0: iconst_2
        # 出棧保存到本地變量1中
    1: istore_1
        # 常量3入棧
    2: iconst_3
        # 出棧保存到本地變量2中
    3: istore_2
        # 局部變量1入棧
    4: iload_1
        # 局部變量2入棧
    5: iload_2
        # 棧頂兩個元素相加,計算結果入棧
    6: iadd
        # 出棧保存到本地變量3中
    7: istore_3
    8: getstatic    #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    11: iload_3
    12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
    15: return
        # 行號表
    LineNumberTable:
        # 源代碼行號: 字節碼行號
        line 5: 0
        line 6: 2
        line 7: 4
        line 8: 8
        line 9: 15
        # 本地變量表
    LocalVariableTable:
    Start  Length  Slot  Name    Signature
           0          16       0     args    [Ljava/lang/String;
           2          14       1          a     I
           4          12       2          b     I
           8            8       3          c     I

4、i++與++i

int i = 0;
int j = i++;
int i = 0;
int j = ++i;
0: iconst_0
1: istore_0
2: iload_0
3: iinc 0, 1
4: istore_1
0: iconst_0
1: istore_0
2: iinc 0, 1
3: iload_0
4: istore_1

Q:下面的方法哪個效率高?

public static void f1(){
    for(int i=0;i<10;i++){
        System.out.println(i);
    }
}
public static void f2(){
    for(int i=0;i<10;++i){
        System.out.println(i);
    }
}

查看字節碼

0: iconst_0
1: istore_0
2: iload_0
3: bipush        10
5: if_icmpge    21
8: getstatic    #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_0
12: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
15: iinc          0, 1
18: goto          2
21: return
0: iconst_0
1: istore_0
2: iload_0
3: bipush        10
5: if_icmpge    21
8: getstatic    #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_0
12: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
15: iinc          0, 1
18: goto          2
21: return

可以看出是一樣的,因此效率一樣。
5、字符串+拼接

效率低 效率高
public static void f1(){
        String str = "A";
        for (int i = 0; i < 10; i++) {
                // 每一次循環都會new一個StringBuilder
                str = str + "A";
        }
        System.out.println(str);
}
public static void f2(){
        // 只要一個StringBuilder
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
                sb.append("A");
        }
        System.out.println(sb);
}
字節碼 字節碼
0: ldc          #2                  // String A
2: astore_0
3: iconst_0
4: istore_1
5: iload_1
6: bipush        10
8: if_icmpge    37
11: new          #3                  // class java/lang/StringBuilder
14: dup
15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
18: aload_0
19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc          #2                  // String A
24: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_0
31: iinc          1, 1
34: goto          5
37: getstatic    #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_0
41: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: return
0: new          #3                  // class java/lang/StringBuilder
3: dup
4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
7: astore_0
8: iconst_0
9: istore_1
10: iload_1
11: bipush        10
13: if_icmpge    29
16: aload_0
17: ldc          #2                  // String A
19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: pop
23: iinc          1, 1
26: goto          10
29: getstatic    #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
32: aload_0
33: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
36: return

6、String Constant Variable
        類、方法、變量儘量指定final修飾
        字符串拼接背後不一定是StringBuilder

public static void f1(){
        final String x = "hello";
        final String y = x + "World";
        String z = x + y;
        System.out.println(z);
}
public void f2(){
        final String x = "hello";
        String y = x + "world";
        String z = x + y;
        System.out.println(z);
}
字節碼 字節碼
0: ldc          #2                  // String hello
2: astore_0
3: ldc          #3                  // String helloWorld
5: astore_1
6: ldc          #4                  // String hellohelloWorld
8: astore_2
9: getstatic    #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_2
13: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
0: ldc          #2                  // String hello
2: astore_1
3: ldc          #7                  // String helloworld
5: astore_2
6: new          #8                  // class java/lang/StringBuilder
9: dup
10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
13: ldc          #2                  // String hello
15: invokevirtual #10                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: aload_2
19: invokevirtual #10                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #11                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: astore_3
26: getstatic    #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
29: aload_3
30: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return

         字符串字面常量(String Literal)

public class StringLiteral {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.println(hello == "Hello");
        System.out.println(Other.hello == hello);
        System.out.println(hello == ("Hel" + "lo"));
        System.out.println(hello == ("Hel" + lo));
        System.out.println(hello == ("Hel" + lo).intern());
    }
    public static class Other{
        public static String hello = "Hello";
    }
}

結果輸出:

true
true
true
false
true

7、常用代碼優化方法
(1)儘量重用對象,不要循環創建對象,比如:for循環字符串拼接;
(2)容器類初始化的時候指定長度,比如:

List<String> collection = new ArrayList<String>(5);
Map<String, String> map = new HashMap<String, String>(32);

          ArrayList底層實現是Object數組,HashMap底層實現是Object數組鏈表
          防止擴容計算
(3)ArrayList隨機遍歷快,LinkedList添加刪除快;
          LinkedList底層實現是雙向鏈表
(4)集合遍歷儘量減少重複計算,比如

for(int i=0, len=collection.size();i<len;i++){}

(5)使用Entry遍歷Map,而不要使用迭代器

for(Map.Entry<String, String> entry : map.entrySet()){
    String key = entry.getKey();
    String value = entry.getValue();
}

(6)大數組複製用System.arraycopy
(7)儘量使用基本類型而不是包裝類型

源代碼 字節碼
public static void main(String[] args) {
        Integer i = 100;
        System.out.println(i);
}
0: bipush        100
2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: getstatic    #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
13: return
public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        System.out.println(i1 == i2);
}
public static void main(String[] args) {
        Integer i3 = 1000;
        Integer i4 = 1000;
        System.out.println(i3 == i4);
}
輸出結果:true 輸出結果:false
i1、i2取的是緩存裏面的,所以相等   i3、i4是重新new的一個Integer,所以不相等
在Integer的valueOf中做了緩存,IntegerCache.low = -128,IntegerCache.high = 127
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

(8)不用手動調用System.gc()
(9)及時消除過期對象的引用,防止內存泄露

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object o) {
        ensureCapacity();
        elements[size++] = o;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
//        return elements[--size];

        Object obj = elements[size-1];
        elements[size-1] = null;//防止內存泄露(斷掉指針)
        size--;

        return obj;
    }

    public void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

(10)儘量使用局部變量,減小變量的作用域
(11)儘量使用非同步的容器ArrayList(^_^)VS Vector(同步)
(12)儘量減小同步作用範圍,synchronized方法 VS 代碼塊(^_^)
(13)ThreadLocal緩存線程不安全的對象,SimpleDateFormat

public class SimpleDateFormatUtil {
    /**
    * 緩存重量級的不安全的對象
    */
    private static ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>(){
        protected SimpleDateFormat initialValue(){
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    public static void main(String[] args){
        dateFormatHolder.get().format(new Date());
    }
}

(14)儘量使用延遲加載

public class Singleton {
    private Singleton(){}
    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

(15)儘量減小使用反射,使用反射時加緩存
(16)儘量使用連接池、線程池、對象池、緩存
(17)及時釋放資源,I/O流、Socket、數據庫連接
(18)慎用異常,不要用拋異常來表示正常的業務邏輯
(19)String操作儘量少用正則表達式
                 replace(^_^)    VS    replaceAll;split
(20)日誌輸出注意使用不同的級別
(21)日誌中參數拼接使用佔位符

不推薦 推薦
log.info("orderId:" + orderId);  log.info("orderId:{}", orderId);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章