JVM基础

原创
2020/08/05 14:18
阅读数 18

  1. Loading
    1. 混合执行 编译执行 解释执行

      1. 检测热点代码:-XX:CompileThreshold = 10000

    2. 自定义类加载器

      1. extends ClassLoader

      2. overwrite findClass() -> defineClass(byte[] -> Class clazz)

    3. ClassLoader的源码

      1. findInCache -> parent.loadClass -> findClass()

    4. 双亲委派,主要出于安全来考虑

    5. Linking

      1. Verification

        1. 验证文件是否符合JVM规定

      2. Preparation

        1. 静态成员变量赋默认值

      3. Resolution

        1. 将类、方法、属性等符号引用解析为直接引用 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

    6. Initializing

      1. 调用类初始化代码,给静态成员变量赋初始值

  2. 小总结:

    1. load - 默认值 - 初始值

    2. new - 申请内存 - 默认值 - 初始值

 

类加载器为什么使用双亲委派:主要是为了安全,如果自定义类加载器,然后自己封装一个java.lang.String包来覆盖原有的,当调用java.lang.String中的方法就不安全,可以获取你输入的内容;‘

类加载器,把磁盘中.class二进制文件加载到内存中,并生成一个Class对象,指向那块内存地址,类加载器加载原理使用双亲委派,从下层类加载器查找类,如果没有,就调用父类加载器,类似于迭代,一层层向上查找,当没有找到就一层层返回然后根据传递的路径进行加载。

类加载器与反射一样,插拔式开发,如果有一个模块想要随时替换,就可以使用反射或者使用类加载器加载,使用这种方式是程序运行以后加载,不是编译阶段加载,可以绕过编译的验证环节。

Class<?> clazz = Demo01ApplicationTests.class.getClassLoader().loadClass("类的路径");
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

重写类加载器,集成ClassLoader类重新findClass方法就可以

public class MSBClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File f = new File("c:/test/", name.replace(".", "/").concat(".class"));
        try {
            FileInputStream fis = new FileInputStream(f);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;

            while ((b=fis.read()) !=0) {
                baos.write(b);
            }

            byte[] bytes = baos.toByteArray();
            baos.close();
            fis.close();//可以写的更加严谨

            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name); //throws ClassNotFoundException
    }

    public static void main(String[] args) throws Exception {
        ClassLoader l = new MSBClassLoader();
        Class clazz = l.loadClass("com.mashibing.jvm.Hello");
        Class clazz1 = l.loadClass("com.mashibing.jvm.Hello");

        System.out.println(clazz == clazz1);

        Hello h = (Hello)clazz.newInstance();//反射
        h.m();

        System.out.println(l.getClass().getClassLoader());
        System.out.println(l.getParent());

        System.out.println(getSystemClassLoader());
    }
}
 

java的执行:解释执行、编译执行(win系统中生成exe本地运行文件),当一个方法被频繁调用,JIT会把这段代码编译执行生成exe提高效率,所以有方法计数器,循环计数器当数值大于阈值就会编译执行,默认是混合模式执行的。

 

热部署:继承ClassLoad重写loadClass方法,而不是重写findClass,findClass打破不了双亲委派的机制,所以热部署失败,不会重新加载

对象的结构

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部