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); |