公司網(wǎng)站去哪里做網(wǎng)站關(guān)鍵詞排名查詢
寫在前面
源碼 。
本文一起看下方法調(diào)用相關(guān)的指令invokexxx以及方法返回(棧幀彈出線程棧)相關(guān)的指令xReturn 。
1:正文
因?yàn)閕nvokexxx指令和普通的指令不同,會(huì)創(chuàng)建一個(gè)新的棧幀,并壓倒操作數(shù)棧中,所以我們首先需要來定義一個(gè)公共的創(chuàng)建棧幀的方法來讓ivvokestatic,invokeinterface等使用,如下:
public class MethodInvokeLogic {public static void invokeMethod(Frame invokerFrame, Method method) {if (method.name.equalsIgnoreCase("returnALong")) {System.out.println("------invoke xxxx 指令,做如下的事情;------");System.out.println("1:從當(dāng)前棧幀中獲取對(duì)應(yīng)的執(zhí)行線程");System.out.println("2:為要執(zhí)行的方法創(chuàng)建對(duì)應(yīng)的棧幀,并將棧幀壓倒線程棧,作為當(dāng)前棧幀");System.out.println("3:如果是有入?yún)⒌脑?#xff0c;則從調(diào)用棧幀的操作數(shù)棧中彈出入?yún)?#xff0c;并設(shè)置到新棧幀的局部變量表中,這樣被調(diào)用方法執(zhí)行時(shí)就可以通過load指令從局部變量中獲取操作");}Thread thread = invokerFrame.thread();Frame newFrame = thread.newFrame(method);thread.pushFrame(newFrame);// 將方法需要的參數(shù)設(shè)置到新棧幀的局部變量表中int argSlotCount = method.argSlotCount();if (argSlotCount > 0) {for (int i = argSlotCount - 1; i >= 0; i--) {Slot slot = invokerFrame.operandStack().popSlot();newFrame.localVars().setSlot(i, slot);}}//hackif (method.isNative()) {if ("registerNatives".equals(method.name())) {thread.popFrame();} else {throw new RuntimeException("native method " + method.name());}}}}
這里我們以invokestatic指令和lreturn指令為例來看下對(duì)應(yīng)的模擬代碼,invokestatic指令:
public class INVOKE_STATIC extends InstructionIndex16 {@Overridepublic void execute(Frame frame) {RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);Method resolvedMethod = methodRef.ResolvedMethod();if (!resolvedMethod.isStatic()) {throw new IncompatibleClassChangeError();}Class clazz = resolvedMethod.clazz();// 確保初始化完成(加載,鏈接,初始化中的初始化,即類加載的最后一步)if (!clazz.initStarted()) {frame.revertNextPC();ClassInitLogic.initClass(frame.thread(), clazz);return;}// 執(zhí)行方法(即生成棧幀并壓入到線程棧)MethodInvokeLogic.invokeMethod(frame, resolvedMethod);}
}
lreturn:
public class LRETURN extends InstructionNoOperands {@Overridepublic void execute(Frame frame) {System.out.println("------lreturn 指令執(zhí)行,做如下的事情:------");System.out.println("1:彈出當(dāng)前的方法棧幀");System.out.println("2:獲取上一個(gè)方法");System.out.println("3:從當(dāng)前方法的操作數(shù)棧中獲取執(zhí)行結(jié)果,并推送到上一個(gè)方法的操作數(shù)棧中");Thread thread = frame.thread();Frame currentFrame = thread.popFrame();Frame invokerFrame = thread.topFrame();long val = currentFrame.operandStack().popLong();// 獲取上一個(gè)方法,并將結(jié)果壓倒其操作數(shù)棧的棧頂,這樣,上一個(gè)就可以通過pop指令獲取結(jié)果invokerFrame.operandStack().pushLong(val);}}
main類:
/*** -Xthejrepath D:\programs\javas\java1.8/jre -Xthetargetclazz D:\test\itstack-demo-jvm-master\tryy-too-simulate-classload-load-clazz\target\test-classes\org\itstack\demo\test\HelloWorld*/
public class Main {public static void main(String[] args) {Cmd cmd = Cmd.parse(args);if (!cmd.ok || cmd.helpFlag) {System.out.println("Usage: <main class> [-options] class [args...]");return;}if (cmd.versionFlag) {//注意案例測(cè)試都是基于1.8,另外jdk1.9以后使用模塊化沒有rt.jarSystem.out.println("java version \"1.8.0\"");return;}startJVM(cmd);}private static void startJVM(Cmd cmd) {// 創(chuàng)建classpathClasspath cp = new Classpath(cmd.thejrepath, cmd.classpath);
// System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());System.out.printf("classpath:%s parsed class:%s \n", cp, cmd.thetargetclazz);//獲取className
// String className = cmd.getMainClass().replace(".", "/");try {
// byte[] classData = cp.readClass(className);/*byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));System.out.println(Arrays.toString(classData));System.out.println("classData:");for (byte b : classData) {//16進(jìn)制輸出System.out.print(String.format("%02x", b & 0xff) + " ");}*/// 創(chuàng)建類加載器準(zhǔn)備加載類/*** 加載3個(gè)階段* 1:加載* 找到字節(jié)碼,并將其存儲(chǔ)到原元空間(<=7方法區(qū)),然后該類,該類父類,父接口也加載并在堆中生成對(duì)應(yīng)的Class對(duì)象* 2:鏈接* 驗(yàn)證:驗(yàn)證文件內(nèi)容的合法性,如是否cafebabe打頭,結(jié)構(gòu)是否符合定義* 準(zhǔn)備:主要是給靜態(tài)變量申請(qǐng)內(nèi)存空間,以及賦初始值,如int,short這種則給默認(rèn)值0* 解析:符號(hào)引用(指向類或者方法的一個(gè)字符串)轉(zhuǎn)換為直接引用(jvm的內(nèi)存地址)* 3:初始化* 執(zhí)行<init>,<clinit>方法,完成靜態(tài)變量的賦值*/ClassLoader classLoader = new ClassLoader(cp);String clazzName = cmd.thetargetclazz.replace(".", "/");Class mainClass = classLoader.loadClass(clazzName);Method mainMethod = mainClass.getMainMethod();new Interpreter(mainMethod, true);/*// 創(chuàng)建className對(duì)應(yīng)的ClassFile對(duì)象ClassFile classFile = loadClass(clazzName, cp);MemberInfo mainMethod = getMainMethod(classFile);if (null == mainMethod) {System.out.println("Main method not found in class " + cmd.classpath);return;}// 核心重點(diǎn)代碼:通過解釋器來執(zhí)行main方法new Interpreter(mainMethod);*/} catch (Exception e) {System.out.println("Could not find or load main class " + cmd.getMainClass());e.printStackTrace();}}/*** 獲取main函數(shù),這里我們要模擬是執(zhí)行器執(zhí)行main函數(shù)的過程,當(dāng)然其他方法也是一樣的!!!* @param classFile* @return*/private static MemberInfo getMainMethod(ClassFile classFile) {if (null == classFile) return null;MemberInfo[] methods = classFile.methods();for (MemberInfo m : methods) {if ("main".equals(m.name()) && "([Ljava/lang/String;)V".equals(m.descriptor())) {return m;}}return null;}/*** 生成class文件對(duì)象* @param clazzName* @param cp* @return*/private static ClassFile loadClass(String clazzName, Classpath cp) {try {// 獲取類class對(duì)應(yīng)的byte數(shù)組byte[] classData = cp.readClass(clazzName);return new ClassFile(classData);} catch (Exception e) {System.out.println("無法加載到類: " + clazzName);return null;}}}
定義需要加載的類:
public class HelloWorld {public static void main(String[] args) {/* long x = fibonacci(10);System.out.println(x);*/returnALong();}public static long returnALong() {long longResult = 99;return longResult;}//斐波那契數(shù)列(Fibonacci sequence)/*private static long fibonacci(long n) {if (n <= 1) {return n;} else {return fibonacci(n - 1) + fibonacci(n - 2);}}*/}
定義main的program argument:
-Xthejrepath
D:\programs\javas\java1.8/jre
-Xthetargetclazz
D:\test\itstack-demo-jvm-master\tryy-too-simulate-invokexxx-and-xreturn\target\test-classes\org\itstack\demo\test\HelloWorld
最后運(yùn)行:
寫在后面
參考文章列表
JVM 虛擬機(jī)字節(jié)碼指令表 。
jvm方法調(diào)用指令invokestatic,invokespecial,invokeinterface,invokevirutal分析 。