深入理解JVM(学习过程) (21)

image-20200214080027838

方法重写(方法的动态分派机制) /** 方法的动态分派 方法的动态分派涉及到一个重要概念:方法接受者。 invokevirtual字节码指令的多态查找流程。 流程: 找到操作数栈顶的第一个元素,对象引用的实际类型。 比较方法重载(overload)与方法重写(overwrite),我们可以得到这样一个结论: 方法重载是静态的,是编译期行为;方法重写是动态的,是运行期行为。 */ public class MyTest6{ psvm(){ Fruit apple = new Apple(); Fruit orange = new Orange(); apple.test(); orange.test(); apple = new Orange(); apple.test(); } } class Fruit(){ public void test(){ sout("fruit"); } } class Apple extends Fruit(){ @Override public void test(){ sout("Apple"); } } class Orange extends Fruit(){ @Override public void test(){ sout("Orange"); } } 输出结果为: apple orange orange

new 关键字的作用: 1、开辟一个内存空间,2、调用构造方法 3、赋值给局部变量

虚方法表与动态分派机制详解

针对于方法调用动态分派的过程,虚拟机会在类的方法区建立一个虚方法表的数据结构。(virtual method table ,简称 vtable),是在加载连接阶段完成的加载。

针对于invokeinterface指令来说,虚拟机会建立一个叫做接口范发表的数据结构。(interface method table , 简称itable)

image-20200214212732468

输出结果:

animal str

Dog date

image-20200214213530201

子类和父类的相同的方法 元素的索引号是一样的。这样会提高性能和效率。

基于栈的指令集与基于寄存器的指令集详细对比 /** 现代JVM在执行Java代码的时候,通常都会将解释执行与编译执行结合起来进行。 所谓解释执行,就是通过解释器来读取字节码,遇到相应的指令就去执行指令。 所谓编译执行,就是通过即时编译器(Just In Time ,JIT)将字节码转换成本地机器执行;现在的JVM会根据代码热点来生成。 基于栈的指令集与基于寄存器的指令集之间的关系: 1. JVM执行指令时所采取的的方式是基于栈的指令集。 2. 基于栈的指令集主要的操作有入栈与出栈两种。 3. 基于栈的指令集的优势在于它可以在不同平台之间移植,而基于寄存器的指令集与操作系统结构紧密关联,无法做到可移植性。 4. 基于栈的指令集的缺点在于完成相同的操作,指令数量通常要比基于寄存器的指令集数量要多;基于栈的指令集是在内存中完成操作的,而基于寄存器的指令集是直接由CPU来执行的,它是在高速缓存区中进行执行的,速度要快很多。虽然虚拟机可以采用一些优化手段,但总体来说,基于栈的指令集的执行速度要慢一些。 */ 举例: 2-1: 在栈中的指令 与 在寄存器中的指令。 栈的指令: 1.iconst_1 (将数字1 压入栈) 2.iconst_2 (将数字2 压入栈) 3.isub (将栈顶的两个元素弹出,执行减法操作,然后将结果压入栈顶) 4.istore (取出栈顶的数据) 寄存器的指令: 1.将2放入到一个寄存器中。 2.调用减法的指令,后边跟一个参数。然后将结果放入到这个寄存器中。 // JVM执行指令时所采取的的方式是基于栈的指令集。举例: public class MyTest8{ public int mCalculate(){ int a = 1; int b = 2; int c = 3; int d = 4; int result = (a+b+c)*D; return result; } 编译上方代码,然后反编译class文件。以字节码的方式查看此运算的过程 iconst_<i>:将int常量推送到操作数栈当中。(i = 0, 1 ,2 ,3 ,4 ,5 ); i 为栈中的索引; istore_<i>:将操作数栈顶的元素弹出。(i = 1 , 2 , 3) istore 4 : (istore 的值最大到3) iload_<i>:从局部变量中加载一个值。(i = 0 ,1 , 2 , 3 ) iload 4 : (iload 的 i的最大值为3 ) iadd: 完成整形的加法。(从操作栈中弹出两个值进行相加,然后把值压入栈顶) isub: 完成数组的减法。(从操作栈中弹出两个值进行相减,然后把值压入栈顶) imul: 完成整数的乘法。(从操作栈中弹出两个值进行相乘,然后把值压入栈顶) 透过字节码生成审视Java动态代理运作机制 //动态代理类的实现。 //动态代理的优势:在真实对象还不存在的情况下就可以把代理对象创建出来,代理对象可以代理多种真实对象。比如说Spring中的APO,我们面面向接口编程的时,通过动态代理给我们生成实例。还有SPring继承的CJLib public interface Subject { public void request(); } public class RealSubject implements Subject { @Override public void request() { System.out.println("From real subject"); } } public class DynamicSubject implements InvocationHandler { private Object sub; public DynamicSubject(Object object) { this.sub = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling: " + method); method.invoke(this.sub, args); System.out.println("after calling: " + method); return null; } } public class Client { public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); RealSubject rs = new RealSubject(); DynamicSubject ds = new DynamicSubject(rs); Class<? extends RealSubject> cls = rs.getClass(); Subject subject = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), cls.getInterfaces(), ds);// 属性文件的设置,是从这个里边找到的。 subject.request(); // 真正指向的实例是 $Proxy System.out.println(subject.getClass()); System.out.println(subject.getClass().getSuperclass()); } } > Task :Client.main() before calling: public abstract void com.erwa.bytecode.Subject.request() From real subject after calling: public abstract void com.erwa.bytecode.Subject.request() class com.sun.proxy.$Proxy0 class java.lang.reflect.Proxy 并且生成了一个$Proxy0文件的class文件: 如下 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // // 如果使用了动态代理,除了eques toString hashCode 的其他方法。并不受动态代理的影响。 An invocation of the hashCode, equals, or toString methods declared in java.lang.Object on a proxy instance will be encoded and dispatched to the invocation handlers invoke method in the same manner as interface method invocations are encoded and dispatched, as described above. The declaring class of the Method object passed to invoke will be java.lang.Object. Other public methods of a proxy instance inherited from java.lang.Object are not overridden by a proxy class, so invocations of those methods behave like they do for instances of java.lang.Object. '如上所述,将对代理实例上java.lang.Object中声明的hashCode,equals或toString方法的调用进行编码,并将其分派到调用处理程序invoke方法,方法与对接口方法调用进行编码和分派的方式相同,如上所述。传递给调用的Method对象的声明类将是java.lang.Object。从java.lang.Object继承的代理实例的其他公共方法不会被代理类覆盖,因此对这些方法的调用的行为就像对java.lang.Object实例的调用一样。' package com.sun.proxy; import com.erwa.bytecode.Subject; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Subject { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { // 只有这一个构造方法 super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void request() throws { // 我们自己要求被代理的方法。 try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { // try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.erwa.bytecode.Subject").getMethod("request"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } java字节码总结

不要把源代码和字节码混为一谈,他们没有相关的对应关系。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfdfy.html