深入理解Java虚拟机程序员Java开发那些事

《深入理解Java虚拟机》读书笔记5:类加载机制与字节码执行引擎

2017-03-19  本文已影响254人  ginobefun

国内JVM相关书籍NO.1,Java程序员必读。读书笔记第五部分对应原书的第七章至第九章,主要介绍虚拟机的类加载机制、字节码执行引擎,并通过实例和实战加深对虚拟机执行子系统这一部分的理解。

第七章 虚拟机类加载机制

7.1 概述

7.2 类加载的时机

7.3 类加载的过程

7.3.1 加载

7.3.2 验证

7.3.3 准备

7.3.4 解析

7.3.5 初始化

7.4 类加载器

7.4.1 类与类加载器

7.4.2 双亲委派模型

双亲委派模型

7.4.3 破坏双亲委派模型

7.5 本章小结

本章介绍了类加载过程的加载、验证、准备、解析和初始化五个阶段中虚拟机进行了哪些动作,还介绍了类加载器的工作原理及其对虚拟机的意义。下一章将一起看看虚拟机如果执行定义在Class文件里的字节码。

第八章 虚拟机字节码执行引擎

8.1 概述

8.2 运行时栈帧结构

运行时栈帧结构

8.2.1 局部变量表

8.2.2 操作数栈

8.2.3 动态连接

8.2.4 方法返回地址

8.2.5 附加信息

方法调用

8.3.1 解析

8.3.2 分派

8.3.3 动态类型语言支持

8.4 基于栈的字节码解释执行引擎

上节主要讲虚拟机是如何调用方法的,这节探讨虚拟机是如何执行方法中的字节码指令的。

8.4.1 解释执行

8.4.2 基于栈的指令集与基于寄存器的指令集

8.4.3 基于栈的解释器执行过程

一段简单的算法代码

    public int calc(){
        int a = 100;
        int b = 200;
        int c = 300;
        return (a + b) * c;
    }

上述代码的字节码表示

public int calc();
Code:
Stack=2, Locals=4, Args_size=1
0:bipush 100
2:istore_1
3:sipush 200
6:istore_2
7:sipush 300
10:istore_3
11:iload_1
12:iload_2
13:iadd
14:iload_3
15:imul
16:ireturn

javap提示这段代码需要深度为2的操作数栈和4个Slot的局部变量空间,作者根据这些信息画了示意图来说明执行过程中的变化情况:

执行偏移地址为0的指令


执行偏移地址为0的指令

执行偏移地址为2的指令


执行偏移地址为2的指令

执行偏移地址为11的指令


执行偏移地址为11的指令

执行偏移地址为12的指令


执行偏移地址为12的指令

执行偏移地址为13的指令


执行偏移地址为13的指令

执行偏移地址为14的指令


执行偏移地址为14的指令

执行偏移地址为16的指令


执行偏移地址为16的指令

注:上面的执行过程仅仅是一种概念模型,虚拟机中解析器和即时编译器会对输入的字节码进行优化。

8.5 本章小结

本章分析了虚拟机在执行代码时,如何找到正确的方法、如何执行方法内的字节码以及执行代码时涉及的内存结构。这第六、七、八三章中,我们针对Java程序是如何存储的、如何载入的以及如何执行的问题进行了讲解,下一章一起看看这些理论知识在具体开发中的经典应用。

第九章 类加载及执行子系统的案例与实战

9.1 概述

9.2 案例分析

9.2.1 Tomcat:正统的类加载器架构

Tomcat服务器的类加载架构

9.2.2 OSGI:灵活的类加载架构

OSGI的类加载架构

9.2.3 字节码生成技术与动态代理的实现

public class DynamicProxyTest {

    interface IHello {
        void sayHello();
    }

    static class Hello implements IHello {
        @Override
        public void sayHello() {
            System.out.println("Hello world");
        }
    }

    static class DynamicProxy implements InvocationHandler {
        Object originalObj;

        Object bind(Object originalObj) {
            this.originalObj = originalObj;
            return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Welcome");
            return method.invoke(originalObj, args);
        }
    }

    public static void main(String[] args) {
        // add this property to generate proxy class file
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        IHello hello = (IHello) new DynamicProxy().bind(new Hello());
        hello.sayHello();
    }
}

9.2.4 Retrotranslator:跨越JDK版本

9.3 实战:自己动手实现远程执行功能

public class JavaClassExecutor {

    public static String execute(byte[] classByte) {
        HackSystem.clearBuffer();
        ClassModifier cm = new ClassModifier(classByte);
        byte[] modifiedBytes = cm.modifyUTF8Constant("java/lang/System", "org/fenixsoft/classloading/execute/HackSystem");
        HotSwapClassLoader hotSwapClassLoader = new HotSwapClassLoader();
        Class clazz = hotSwapClassLoader.loadByte(modifiedBytes);
        try {
            Method method = clazz.getMethod("main", new Class[]{String[].class});
            method.invoke(null, new String[]{null});
        } catch (Throwable t) {
            t.printStackTrace(HackSystem.out);
        }

        return HackSystem.getBufferString();
    }
}

用于测试的JSP

<%@page import="java.lang.*" %>
<%@page import="java.io.*" %>
<%@page import="org.fenixsoft.classloading.execute.*" %>

<%
InputStream is = new FileInputStream("c:/TestClass.class");
byte[] b = new byte[is.available()];
is.read(b);
is.close();

out.println(JavaClassExecutor.execute(b));

%>

9.4 本章小结

只有了解虚拟机如何执行程序,才能更好地理解怎样写出优秀的代码。

系列读书笔记

扫一扫 关注我的微信公众号
上一篇 下一篇

猜你喜欢

热点阅读