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 |
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 { public Stack() { public void push(Object o) { public Object pop() { Object obj = elements[size-1]; public void ensureCapacity() { |
(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); |