今日份学习:JVM和字节码

JVM与字节码原理

Java 1995. Sun micro systems

JLS与JVMS

  • Java语言规范 Java Language Specification
    • 定义Java语言的语法
  • Java编译器:两者之间的桥梁
  • Java虚拟机规范 Java Virtual Machine Specification
    • 定义字节码如何在JVM中执行


JVM的基本结构

  • HotSpot JVM:Architecture



JVM的运行时内存区域

    • 所有对象都在堆上分配
    • 只能操作对象的引用(地址)
    • 只能控制对象的诞生,但是无法控制它的死亡
  • 方法栈

    • 线程私有
    • 每当一个方法调用发生,方法就入栈一个栈帧
    • 每当方法退出(返回或者异常),栈帧就被弹出
    • 局部变量在栈帧中分配
    • 当栈深度过大时,抛出StackOverflowError异常
  • 本地方法栈

  • 栈帧

    • 局部变量表
    • 操作数栈

字节码是如何工作的?

  • 方法区

    • 被征个虚拟机所共享的Class信息
    • 运行时常量池
    • Java7之前:永久代(PermGen)
      • OutOfMemory:Perm gen
    • Java8之后:元空间(Metaspace)
      • 元空间和Native共享内存



字节码

字节码的结构

  • 查看和阅读字节码
    • javap
    • 图形化工具:例如Classpy
    • 反编译器

字节码的加载和执行

The Java® Virtual Machine Specification — Java SE 8 Edition

JVM看到的major_version比自己的高,会拒绝。UnsupportedClassVersionError:Unsupported major.minor version xxx.



类加载器

  • 类加载器打通了代码和数据间的障碍
    • 动态加载代码
    • 动态生成代码
    • 动态替换代码
  • 使用场景
    • Mock
    • AOP
    • 热部署/热替换
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Scanner;

class DynamicJson {
    // 假设目录中有fastjson-1.2.61.jar和fastjson-1.2.62.jar

    public static void main(String[] args)
            throws MalformedURLException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
            NoSuchMethodException, SecurityException, ClassNotFoundException, NoSuchFieldException {
        System.out.println("输入想要使用的fastjson版本");

        Scanner scanner = new Scanner(System.in);
        String version = scanner.next();
        System.out.println(version);

        File jar = new File("fastjson-" + version + ".jar");
        if (jar.isFile()) {
            ClassLoader cl = new URLClassLoader(new URL[] { jar.toURI().toURL() });
            // com.alibaba.fastjson.JSON.toJSONString(Object)
            Class jsonClass = cl.loadClass("com.alibaba.fastjson.JSON");
            System.out.println(jsonClass.getMethod("toJSONString", Object.class).invoke(null, new ArrayList()));
            System.out.println(jsonClass.getField("VERSIION"));
        } else {
            System.out.println("Not Found");
        }
    }
}

双亲委派加载模型(Java8)

  • 越核心的东西越是由父加载器(层次越高的加载器)加载的。

  • 如果加载一个类的时候它的父类还没有被加载,那么先加载它的父类。

为什么需要由双亲委派加载模型?

  • 1. 安全性

    • 不希望加载一个恶意的字节码

问题:如果我们自己写的java,lang.object是不会执行还是会被已经加载的覆盖?

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

class MyBadClassloadere extends ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        new MyBadClassloadere().loadClass("text.java");

    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = Files.readAllBytes(new File(name + ".class").toPath());
            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException("", e);
        }
    }
}

结果
java.lang.SecurityException: Prohibited package name: java.lang


  • 2. 正确性

    • 类加载也被instanceof所检查
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

class MyBadClassloadere extends ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        // 这里会显示不能转换,证明不是同一种类
        Test test = (Test) new MyBadClassloadere().loadClass(Test.class.getName()).newInstance();

        // 这里也能证明它们不是同一种类
        Object test = new MyBadClassloadere().loadClass(Test.class.getName()).newInstance();
        System.out.println("instanceof test: " + (test instanceof Test));
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.contains("test")) {
            try {
                byte[] bytes = Files.readAllBytes(new File("test.class").toPath());
                return defineClass(name, bytes, 0, bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ClassNotFoundException("", e);
            }
        } else {
            return super.loadClass(name);
        }
    }
}



JIT(Just in time) Compiler

理解JIT编译器

外界和JVM内部的桥梁

两种执行方式:

  • 解释执行

    • 方便、跨平台
  • 编译执行

    • 麻烦、不灵活、编译后的代码臃肿

  • 选项-XX:+PrintCompilation可以打开编译日志,当JVM对方法进行编译的时候,都会打印一行信息,什么方法被编译了。

现在好像是

  • 混合模式?








题外话:编译器的前端和后端

这里的“前端(Front End)”指的是编译器对程序代码的分析和理解过程。它通常只跟语言的语法有关,跟目标机器无关。而与之对应的“后端(Back End)”则是生成目标代码的过程,跟目标机器有关。

posted @ 2020-03-13 09:26  带了1个小才艺  阅读(59)  评论(0编辑  收藏  举报