深入JVM内核(十)——字节码执行
由于之前看的容易忘记,因此特记录下来,以便学习总结与更好理解,该系列博文也是第一次记录,所有有好多不完善之处请见谅与留言指出,如果有幸大家看到该博文,希望报以参考目的看浏览,如有错误之处,谢谢大家指出与留言。
目录
1、javap
2、简单的字节码执行过程
3、常用的字节码
4、使用ASM生成Java字节码
5、JIT及其相关参数
6、总结—路漫漫其修远兮 吾将上下而求索
1、javap
— class文件反汇编工具
![70][]
— javap –verbose Calc
public int calc();
Code:
Stack=2, Locals=4, Args_size=1
0: sipush 500
3: istore_1
4: sipush 200
7: istore_2
8: bipush 50
10: istore_3
11: iload_1
12: iload_2
13: iadd
14: iload_3
15: idiv
16: ireturn
}
2、简单的字节码执行过程
1、void setAge(int) 方法的字节码
2、2A 1B B5 00 20 B1
3、2A _aload_0
无参
将局部变量slot0 作为引用 压入操作数栈
4、1B _iload_1
无参
将局部变量slot1 作为整数 压入操作数栈
5、B5 _putfield
设置对象中字段的值
参数为2bytes (00 20) (指明了字段)
指向常量池的引用
Constant\_Fieldref
此处为User.age
弹出栈中2个对象:objectref, value
将栈中的value赋给objectref的给定字段
6、B1 _return
3、常用的字节码
1、常量入栈
![70 9][]
2、局部变量压栈
xload(x为i l f d a)
分别表示int,long,float,double,object ref
xload\_n(n为0 1 2 3)
xaload(x为i l f d a b c s)
分别表示int, long, float, double, obj ref ,byte,char,short
从数组中取得给定索引的值,将该值压栈
iaload
执行前,栈:..., arrayref, index
它取得arrayref所在数组的index的值,并将值压栈
执行后,栈:..., value
3、出栈装载入局部变量
xstore(x为i l f d a)
出栈,存入局部变量
xstore\_n(n 0 1 2 3)
出栈,将值存入第n个局部变量
xastore(x为i l f d a b c s)
将值存入数组中
iastore
执行前,栈:...,arrayref, index, value
执行后,栈:...
将value存入arrayref\[index\]
4、通用栈操作(无类型)
nop
pop
弹出栈顶1个字长
dup
复制栈顶1个字长,复制内容压入栈
5、类型转化
![70 10][]
6、i2l
将int转为long
执行前,栈:..., value
执行后,栈:...,result.word1,result.word2
弹出int,扩展为long,并入栈
7、整数运算
![70 11][]
8、浮点运算
![70 12][]
9、对象操作指令
— new
— getfield
— putfield
— getstatic
— putstatic
10、条件控制
— ifeq 如果为0,则跳转
— ifne 如果不为0,则跳转
— iflt 如果小于0 ,则跳转
— ifge 如果大于0,则跳转
— if\_icmpeq 如果两个int相同,则跳转
11、ifeq
— 参数 byte1,byte2
— value出栈 ,如果栈顶value为0则跳转到(byte1<<8)|byte2
— 执行前,栈:...,value
— 执行后,栈:...
12、方法调用
— invokevirtual
— invokespecial
— invokestatic
— invokeinterface
— xreturn(x为 i l f d a 或为空)
4、使用ASM生成Java字节码
1、ASM
— Java字节码操作框架
— 可以用于修改现有类或者动态产生新类
— 用户
AspectJ
Clojure
Ecplise
spring
cglib
— hibernate
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
cw.visit(V1_7, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mw.visitVarInsn(ALOAD, 0); //this 入栈
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mw.visitInsn(RETURN);
mw.visitMaxs(0, 0);
mw.visitEnd();
mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mw.visitLdcInsn("Hello world!");
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
mw.visitEnd();
byte[] code = cw.toByteArray();
AsmHelloWorld loader = new AsmHelloWorld();
Class exampleClass = loader
.defineClass("Example", code, 0, code.length);
exampleClass.getMethods()[0].invoke(null, new Object[] { null });
2、模拟实现AOP字节码织入
在函数开始部分或者结束部分嵌入字节码
可用于进行鉴权、日志等
我们需要嵌入的内容
![70 14][]
3、
class AddSecurityCheckClassAdapter extends ClassVisitor {
public AddSecurityCheckClassAdapter( ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
// 重写 visitMethod,访问到 "operation" 方法时,
// 给出自定义 MethodVisitor,实际改写方法内容
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
MethodVisitor wrappedMv = mv;
if (mv != null) {
// 对于 "operation" 方法
if (name.equals("operation")) {
// 使用自定义 MethodVisitor,实际改写方法内容
wrappedMv = new AddSecurityCheckMethodAdapter(mv);
}
}
return wrappedMv;
}
}
class AddSecurityCheckMethodAdapter extends MethodVisitor {
public AddSecurityCheckMethodAdapter(MethodVisitor mv) {
super(Opcodes.ASM5,mv);
}
public void visitCode() {
visitMethodInsn(Opcodes.INVOKESTATIC, "geym/jvm/ch10/asm/SecurityChecker",
"checkSecurity", "()Z");
super.visitCode();
}
}
public class Generator{
public static void main(String args[]) throws Exception {
ClassReader cr = new ClassReader("geym.jvm.ch10.asm.Account");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
AddSecurityCheckClassAdapter classAdapter = new AddSecurityCheckClassAdapter(cw);
cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
byte[] data = cw.toByteArray();
File file = new File("bin/geym/jvm/ch10/asm/Account.class");
FileOutputStream fout = new FileOutputStream(file);
fout.write(data);
fout.close();
}
}
SecurityChecker.checkSecurity …
operation….
5、JIT及其相关参数
1、字节码执行性能较差,所以可以对于热点代码编译成机器码再执行,在运行时的编译, 叫做JIT Just-In-Time
2、JIT的基本思路是,将热点代码,就是执行比较频繁的代码,编译成机器码。
3、JIT
public class JITTest {
public static void met(){
int a=0,b=0;
b=a+b;
}
public static void main(String[] args) {
for(int i=0;i<1000;i++){
met();
}
}
}
-XX:CompileThreshold=1000
-XX:+PrintCompilation
4、-Xint
解释执行
5、-Xcomp
全部编译执行
6、-Xmixed
默认,混合
还没有评论,来说两句吧...