解释执行与编译执行区别,栈的入栈出栈详解,寄存器与栈指令区别,透过字节码审视动态代理

解释执行与编译执行

现代JVM在执行Java代码的时候,通常都会将解释执行与编译执行二者结合起来进行。

  • 所谓解释执行,就是通过解释器(类似流)来读取字节码,遇到相应的指令就去执行该指令。
  • 所谓编译执行,就是通过即时编译器(Just In Time,JIT)将字节码转换为本地机器码;现代JVM会根据代码热点来生成相应的本地机器码。

栈的指令集与基于寄存器的指令集之间的关系

  • 1.JVM执行指令时所采取的方式是基于栈的指令集。
  • 2.基于栈的指令集主要的操作有入栈和出栈两种。
  • 3.基于栈的指令集的优势在于它可以在不同平台之间移植,而基于寄存器的指令集是与硬件架构紧密关联的,无法做到可移植
  • 4.基于栈的指令集的缺点在于完成相同的操作,指令数量通常要比基于寄存器的指令数量要多;基于栈的指令集是在内存中完成操作的,而基于寄存器的指令集是直接由CPU来执行的,它是在高速缓冲区中进行执行的,速度要快很多。虽然虚拟机可以采用一些优化手段, 但总体来说,基于栈的指令集执行速度要慢一些。

示例

public class MyTest8 {
    
    

    /**
     * 例如 2-1的操作
     * <p>
     * 基于栈
     * <p>
     * 首先压栈iconst_1 对应1的数据
     * 再压栈iconst_2 对应2的数据
     * 此时 调用isub 将栈顶对应的2 1 弹出
     * 执行 2-1 操作 将结果重新压入栈顶
     * istore_0 将2-1的结果赋值到局部变量表的第1个局部变量
     * <p>
     * 基于寄存器
     * 将2 放入寄存器 mov
     * sub 减法指令 跟着参数 1 就得到结果放回寄存器
     */

    public int myCalculate() {
    
    
        int a = 1;
        int b = 2;
        int c = 3;
        int d = 4;

        int result = (a + b - c) * d;

        return result;

    }

    public static void main(String[] args) {
    
    
        int i = new MyTest8().myCalculate();
        System.out.println(i);
    }
}

通过jclasslib进行编译
基于栈的指令集
在这里插入图片描述

//最大栈的深度为2 意思就是栈中最多储存两个数据
max_stack : 2
//最大的局部变量个数为6
max_locals: 6
code
//将int常量<i>(-1、0、1、2、3、4或5)压入操作数栈顶。对应对应数字 字节码使用更精简的方式
//bipush 1 等价 iconst_1 压入操作数栈顶
 0 iconst_1
 //将栈顶元素弹出(常量1) 弹出栈 将弹出来的值赋值给局部变量表的索引为1的局部变量
 1 istore_1
 2 iconst_2
 3 istore_2
 4 iconst_3
 5 istore_3
 6 iconst_4
 //istore  来说只精简到3这个位置后续的就得执行如istore 4的命令
 7 istore 4
 //加载局部变量表中索引为1的局部变量 将值推送到栈顶
 9 iload_1
  //加载局部变量表中索引为2的局部变量 将值推送到栈顶
10 iload_2
//取出栈顶和及下的 数据 将其弹出  此时栈只有 并将两者相加的值 推送到栈顶 2+1 = 3
11 iadd
//加载局部变量表中索引为3的局部变量 将值推送到栈顶
12 iload_3
//取出栈顶和及下的 数据 将其弹出 此时栈只有 并将两者相减的值 推送到栈顶 3-3 = 0
13 isub
  //加载局部变量表中索引为4的局部变量 将值推送到栈顶
14 iload 4
//取出栈顶和及下的 数据 将其弹出 此时栈只有 并将两者相乘的值 推送到栈顶 0*3 = 0
16 imul
 //将栈顶元素弹出(0) 弹出栈 将弹出来的值赋值给局部变量表的索引为5的局部变量 此时栈中无数据
17 istore 5
 //加载局部变量表中索引为5的局部变量 将值推送到栈顶 此时栈只有 局部变量表索引为5的数据 0
19 iload 5
//将栈顶的值弹出 将栈顶的值return给调用者 如果栈中还有其他值 全部丢弃
//ireturn返回int类型
21 ireturn

在这里插入图片描述

基于字节码的动态代理

1.构建接口

public interface IsubJect {
    
    
    void test();
}

2.构建实现类

public class RealSubject implements IsubJect {
    
    

    @Override
    public void test() {
    
    
        System.out.println("IsunJect Test");
    }
}

3.创建执行器


public class DynamicSubject implements InvocationHandler {
    
    

    private Object subject;

    public DynamicSubject(Object subject) {
    
    
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    


        System.out.println("kaishi!");

        method.invoke(subject, args);
        System.out.println("jieshu!");

        return null;
    }
}

4.编写客户端

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //将生成的真实对象写入磁盘
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        RealSubject realSubject = new RealSubject();
        InvocationHandler dynamicSubject = new DynamicSubject(realSubject);
        //本质上jdk底层调用本地方法defineClass0 将生成对应字节流转换成Class对象返回 jvm底层本质上底层解析的数据都是字节流数组 byte[]
        IsubJect isubJect = (IsubJect) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), dynamicSubject);
        //抽象方法的调用都会转向DynamicSubject当中
        isubJect.test();
        System.out.println(isubJect.getClass()); //真实的对象 运行期动态生成的
        System.out.println(isubJect.getClass().getSuperclass()); //真实的对象父类

    }
}

打印文件

kaishi!
IsunJect Test
jieshu!
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy

newProxyInstance 创建实例

使用newProxyInstance 来构建动态代理对象
截取核心代码


        /*
         *获取到对应的动态代理Class对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
    
    
            if (sm != null) {
    
    
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
    
    
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
    
    
                    public Void run() {
    
    
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //传入执行器通过构造方法实例化对象
            return cons.newInstance(new Object[]{
    
    h});

getProxyClass0 获取ProxyClass

 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
    
    
        if (interfaces.length > 65535) {
    
    
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //如果由给定的装入器定义的代理类实现
        //给定缓存的接口只返回这个缓存对象;(底层其实将对应的对象缓存到concurrentMapd当中)
        //否则,它将通过ProxyClassFactory创建代理类
        return proxyClassCache.get(loader, interfaces);
    }

缓存忽略,具体研究是如何通过ProxyClassFactory创建代理类Class对象
在这里插入图片描述
点击进入可以看到我们生产的代理对象的标识
在这里插入图片描述
生成Class对象

ProxyClassFactory构造方法


            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 获取到编译完成后的字节流里面记录了代理对象的全部信息 
             * 传入拼好的 name 接口集合 和访问标识
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
    
    
            //调用本地方法传入加载器将字节流文件生成Class对象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
    
    
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }

ProxyGenerator.generateProxyClass 生成字节流

jdk在底层调用可供内部使用的一个方法

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    
    
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        //generateClassFile真正的解析字节流数据 类似自定义classLoader中的loadClassDate读取的数据
        final byte[] var4 = var3.generateClassFile();
        //改地方会通过一个系统属性判断是否将生成的class文件写到本地磁盘
        if (saveGeneratedFiles) {
    
    
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
    
    
                public Void run() {
    
    
                    try {
    
    
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
    
    
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
    
    
                            var2 = Paths.get(var0 + ".class");
                        }
						//true 写入
                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
    
    
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

系统属性:sun.misc.ProxyGenerator.saveGeneratedFiles
在这里插入图片描述

在这里插入图片描述

generateClassFile该方法是真正将数据转换成字节流

在这里插入图片描述
以上 我们得到了动态代理的class对象

此时我们可以通过该系统属性得到对应个动态代理的class文件 文件内容如下

package com.sun.proxy;

import com.example.demo.com.jvm.bytecode.proxy.IsubJect;
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 IsubJect {
    
    
	//对应的method方法对象 具体方法对象可以看静态代码块
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
    
    
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
    
    
        try {
    
    
        //h就是我们的InvocationHandler执行器 编译期间已经将当前执行器赋值上
        //意思就是调用其唯一的invoke方法 传入(当前对象proxy0,equals方法对象,对应的jvm参数)
        Method.invoke(实现类对象,)
            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 void test() throws  {
    
    
        try {
    
    
        //同上
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
    
    
            throw var2;
        } catch (Throwable var3) {
    
    
            throw new UndeclaredThrowableException(var3);
        }
    }

    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 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 {
    
    
        	//Object的equals对象
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            //我们要代理的接口中的test方法对象
            m3 = Class.forName("com.example.demo.com.jvm.bytecode.proxy.IsubJect").getMethod("test");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
    
    
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
    
    
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

由上述生成的字节码我们可以看出生成出来的代理对象
$Proxy0 extends Proxy implements IsubJect(自定义接口) 重写可Object的三个方法equals,hashCode,toString 和实现的的接口方法集合本例是test一个方法method对象,也就是说方法在压栈的时候字节码会动态分派去子类寻找对应的方法。而equals,hashCode,toString 和实现的的接口方法集合会去执行InvocationHandler中的invoke方法其实的如clone等方法并不会执行
在这里插入图片描述
只执行了equals的一次

kaishi!
Exception in thread "main" java.lang.IllegalMonitorStateException
jieshu!
	at java.lang.Object.notify(Native Method)
====
	at com.example.demo.com.jvm.bytecode.proxy.Client.main(Client.java:21)

class对象拿到之后通过构造方法拿到对应的构造方法.newInstance 创建对应的实例
在这里插入图片描述
在这里插入图片描述

总结

动态代理底层在首先判断是否有缓存,有直接返回没有通过ProxyClassFactory创建动态代理proxy0的Class对象,底层会重写实现接口的所有方法以及Object的equals,hashCode,toString方法,然后通过Class对象获取 proxy0的构造方法调用newInstance实例化对象

CGlib和JDK动态代理的区别

1.JDK动态代理底层是使用InvocationHandler 调用Proxy.newProxyInstance方法(classLoader,interfaces,invocationhandler)来生成一个动态代理对象proxy0 proxy0继承proxy 实现interfaces
2.cglib 底层采用的是继承方法,重写被代理的对象重新被代理的对象方法,程序在调用父类的方法时由于子类重写了父类方法 虚方法表对应的方法入口就会指向子类的方法此时我们就可以对方法进行扩展同时也能调用父类的方法

猜你喜欢

转载自blog.csdn.net/qq_42261668/article/details/108714725