前面我们通过tomcat本身的参数以及jvm的参数对tomcat做了优化,详情查看:tomcat优化,其实要想将应用程序跑的更快,效率更高,除了对tomcat容器以及jvm优化外,应用程序代码本身如果写的效率不高的,那么也是不行的,所以对于程序本身的优化也就很重要了。 对于程序本身的优化,可以借鉴很多前辈的经验,但是有些时候,在从源码角度分析的话,不好鉴别出哪个效率高,如对字符串拼接的操作,是直接“+”号拼接效率高还是使用StringBuilder效率高呢? 这个时候,就需要通过查看编译好的class文件中的字节码,就可以找到答案。 我们都知道,java编写应用,需要先通过javac命令编译成class文件,在通过jvm执行,jvm执行时是需要将class文件中的字节码载入到jvm进行运行的 首先,看一下简单的Test类的代码: 通过javap命令查看class文件中的字节码内容: 当我们运行javap命令后会得到一个 生成的内容虽然看上去很多,但是总体来说,大致分为4个部分: 第一部分: 显示生成这个class的java源文件、版本信息、生成时间等 这么看的话我们是很难看懂里面的内容说的是什么的,我们需要对里面的参数一一作出说明,请往下看。 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140 api文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2 示例: 方法的方法描述符: int a = 2: 我们都知道,i++表示,先返回再+1,++i表示,先+1再返回,那么它的底层是怎么样的呢?我们一起来探究一下 测试代码: 通过 命令 图解: 图解: 优化,不仅仅是在运行环境中进行优化,还需要在代码本身做优化,如果代码本身存在性能问题,那么在其他方面再怎么优化也不可能达到效果最优的 调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量,实例变量等,都在堆中创建,速度较慢,另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收 异常对性能不利,抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,手机调用跟踪信息,只要有异常被抛出,java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象,异常只能用于错误处理,不应该用来控制程序流程 反射是java提供给用户的一个很强大的功能,功能强大往往意味着效率不高,不建议在程序运行过程中使用尤其是频繁使用反射机制,特别是Method的invoke方法 这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁的创建和销毁线程 使用字节码的方式可以很好的查看代码底层的执行,从而可以看出那些实现效率高,那些实现效率低,可以更好的对我们代码做优化,让程序执行效率更高,今天的内容就到这里了,感兴趣的小伙伴记得关注我,大家加油~
一、前言
二、通过javap命令查看class文件的字节码内容
public class Test { public static void main(String[] args) { int a = 2; int b = 5; int c = b - a; System.out.println(c); } }
javap -v Test.class > Test.txt
用法: javap <options> <classes> 其中, 可能的选项包括: -help --help -? 输出此用法消息 -version 版本信息 -v -verbose 输出附加信息 -l 输出行号和本地变量表 -public 仅显示公共类和成员 -protected 显示受保护的/公共类和成员 -package 显示程序包/受保护的/公共类 和成员 (默认) -p -private 显示所有类和成员 -c 对代码进行反汇编 -s 输出内部类型签名 -sysinfo 显示正在处理的类的 系统信息 (路径, 大小, 日期, MD5 散列) -constants 显示最终常量 -classpath <path> 指定查找用户类文件的位置 -cp <path> 指定查找用户类文件的位置 -bootclasspath <path> 覆盖引导类文件的位置
Test.txt
的文件
内容如下:# 显示生成这个class的java源文件、版本信息、生成时间等 Classfile /F:/project/test/target/classes/com/lyy/Test.class Last modified 2020-5-7; size 562 bytes MD5 checksum 58100edcdfebfd9769cdbb1b634baf3c Compiled from "Test.java" public class com.lyy.Test minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPER # 显示了该类中所涉及的常量池,共35个常量 Constant pool: #1 = Class #2 // com/lyy/Test #2 = Utf8 com/lyy/Test #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Methodref #3.#9 // java/lang/Object."<init>":()V #9 = NameAndType #5:#6 // "<init>":()V #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/lyy/Test; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Fieldref #17.#19 // java/lang/System.out:Ljava/io/PrintStream; #17 = Class #18 // java/lang/System #18 = Utf8 java/lang/System #19 = NameAndType #20:#21 // out:Ljava/io/PrintStream; #20 = Utf8 out #21 = Utf8 Ljava/io/PrintStream; #22 = Methodref #23.#25 // java/io/PrintStream.println:(I)V #23 = Class #24 // java/io/PrintStream #24 = Utf8 java/io/PrintStream #25 = NameAndType #26:#27 // println:(I)V #26 = Utf8 println #27 = Utf8 (I)V #28 = Utf8 args #29 = Utf8 [Ljava/lang/String; #30 = Utf8 a #31 = Utf8 I #32 = Utf8 b #33 = Utf8 c #34 = Utf8 SourceFile #35 = Utf8 Test.java #显示该类的构造器,编译器自动插入的 { public com.lyy.Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/lyy/Test; # 显示了main方的信息(这个是我们需要重点关注的) public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_2 1: istore_1 2: iconst_5 3: istore_2 4: iload_2 5: iload_1 6: isub 7: istore_3 8: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_3 12: invokevirtual #22 // Method java/io/PrintStream.println:(I)V 15: return LineNumberTable: line 6: 0 line 7: 2 line 8: 4 line 9: 8 line 10: 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 } SourceFile: "Test.java"
第二部分: 显示了该类中所涉及的常量池,共35个常量
第三部分: 显示该类的构造器,编译器自动插入的
第四部分: 显示了main方的信息(这个是我们需要重点关注的)三、常量池
Constant Type
Value
说明
CONSTANT_Class
7
类或接口的符号引用
CONSTANT_Fieldref
9
字段的符号引用
CONSTANT_Methodref
10
类中方法的符号引用
CONSTANT_InterfaceMethodref
11
接口中方法的符号引用
CONSTANT_String
8
字符串类型常量
CONSTANT_Integer
3
整形常量
CONSTANT_Float
4
浮点型常量
CONSTANT_Long
5
长整型常量
CONSTANT_Double
6
双精度浮点型常量
CONSTANT_NameAndType
12
字段或方法的符号引用
CONSTANT_Utf8
1
UTF-8编码的字符串
CONSTANT_MethodHandle
15
表示方法句柄
CONSTANT_MethodType
16
标志方法类型
CONSTANT_InvokeDynamic
18
表示一个动态方法调用点
四、描述符
4.1 字段描述符
术 语
类型
描述
B
byte
signed byte
C
char
Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
D
double
double-precision floating-point value
F
float
single-precision floating-point value
I
int
integer
J
long
long integer
L
ClassName ;
reference an instance of class ClassName
S
short
signed short
Z
boolean
true or false
[
reference
one array dimension
4.1 方法描述符
4.2 解读方法字节码
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V //方法描述,V表示该方法的返回值为void flags: ACC_PUBLIC, ACC_STATIC //方法修饰符,public、static的 Code: //stack=2 操作栈的大小为0 //locals=4 本地变量表大小 //args_size=1 参数的个数 stack=2, locals=4, args_size=1 0: iconst_2 //将数字2值压入操作栈,位于栈的最上面 1: istore_1 //从操作栈中弹出一个元素(数字2),放入到本地变量表中,位于下标为1的位置(下标为0的是this) 2: iconst_5 //将数字5值压入操作栈,位于栈的最上面 3: istore_2 //从操作栈中弹出一个元素(5),放入本地变量表中,位于第下标为2个位置 4: iload_2 //将本地变量表中下标为2的位置压入操作栈(5) 5: iload_1 //将本地变量表中下标为1的位置压入操作栈(2) 6: isub //操作栈中的2个数字相减 7: istore_3 //将相减的结果压入到本地变量表中,位于下标为3的位置 //通过#16号找到对应的常量,即可找到对应的引用 8: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_3 //将本地变量表中下标为3的位置元素压入操作栈(3) // 通过#22找到对应的常量,即可找到对应的引用,进行方法调用 12: invokevirtual #22 // Method java/io/PrintStream.println:(I)V 15: return //返回 LineNumberTable: //行号的列表 line 6: 0 line 7: 2 line 8: 4 line 9: 8 line 10: 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 } SourceFile: "Test.java"
4.3 图解方法字节码:
int b = 5:
int c = b – a:
五、i++ 与 i++ 的不同
public static void main(String[] args) { new Test2().method1(); new Test2().method2(); } public void method1(){ int i = 1; int a = i++; System.out.println(a); } public void method2(){ int i = 1; int a = ++i; System.out.println(a); }
5.1 查看 class字节码
javap -v Test2.class > Test2.txt
,查看class字节码 public void method1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 1: istore_1 2: iload_1 3: iinc 1, 1 6: istore_2 7: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 11: invokevirtual #31 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 11: 0 line 12: 2 line 13: 7 line 14: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/lyy/Test2; 2 13 1 i I 7 8 2 a I public void method2(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 1: istore_1 2: iinc 1, 1 5: iload_1 6: istore_2 7: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 11: invokevirtual #31 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 17: 0 line 18: 2 line 19: 7 line 20: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/lyy/Test2; 2 13 1 i I 7 8 2 a I } SourceFile: "Test2.java"
5.2 对比
5.2.1 i++
0: iconst_1 //将数字1压入操作栈 1: istore_1 //将数字1从操作栈弹出,压入到本地变量表中,下标为1 2: iload_1 //从本地变量表中获取下标为1的数据,压入到操作栈中 3: iinc 1, 1 //将本地变量中的1,再+1 6: istore_2 //将数字1从操作栈中弹出,压入到本地变量表中,下标为2 7: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 //从本地变量表中获取下标为2的数据,压入到操作栈中 11: invokevirtual #31 // Method java/io/PrintStream.println:(I)V 14: return
i++打印结果:15.2.2 ++i
0: iconst_1 //将数字1压入到操作栈 1: istore_1 //将数字1从操作栈弹出,压入到本地变量表中,下标为1 2: iinc 1, 1//将本地变量中的1,再+1 5: iload_1 //从本地变量表中获取下标为1的数据(2),压入到操作栈中 6: istore_2 //将数字2从操作栈弹出,压入到本地变量表中,下标为2 7: getstatic #25 // Field 10: iload_2 //从本地变量表中获取下标为2的数据(2),压入到操作栈中 11: invokevirtual #31 // Method 14: return
5.3 区别
六、代码优化
6.1 尽可能使用局部变量
6.2 尽量减少对变量的重复计算
for(int i = 0; i< list.size;i++) {...} #建议替换为: int length = list.size(); for(int i = 0; i< length;i++) {...}
6.3 异常不应该用来控制程序流程
6.4 程序运行过程中避免使用反射
如果确实有必要使用,一种建议性的做法就是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存6.5 使用数据库连接池和线程池
6.6 ArrayList 随机遍历快,LinkedList添加删除快
七、小结
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算