zoukankan      html  css  js  c++  java
  • 2-详解class加载过程

    一.class加载过程

    Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验/准备/解析初始化,最终

    形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制

    loading -> linking (verification-> preparation -> resolution )-> initializing

    loading:把class文件load到内存中,采用双亲委派,主要是为了安全性

    verification:校验class文件是否符合标准

    preparation:静态变量分配内存并设初始值的阶段(不包括实例变量)

    resolution:把符号引用转换为直接引用

    initializing:静态变量赋初始值

    遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类型没有进行过初始

    化,则需要先触发其初始化阶段。

    能够生成这四条指令的典型Java代码场景有:

    1.使用new关键字实例化对象。

    2.读取或设置一个类型的静态字段。(被final修饰、已在编译期把结果放入常量池的静态字段除外,但是

    final static int i=10;如果没有=10还是会在准备阶段给它赋默认值,在初始化的时候赋值。)

    3.调用一个类型的静态方法。

    4.使用java.lang.reflect包的方法对类型进行反射调用。

    5.虚拟机启动的时候被执行的主类必须初始化。

    6.初始化子类时,父类首先初始化。

    二.类加载器

    类加载器实现了loading这个动作,把class文件加载到内存。

    1.启动类加载器(BootstrapClassLoader):负责加载存放在lib/rt.jar charset.jar 等目录下的核心类,由c++实现。启动类加载器无法被Java程序直接引用(classLoader的loadClass方法中,若parent为 null则使用启动类加载器。

    2.扩展类加载器(ExtensionClassLoader):负责加载lib/ext目录下的类,在类sun.misc.Launcher$ExtClassLoader 中以Java代码的形式实现 。

    3.应用程序类加载器(ApplicationClassLoader):负责加载用户类路径(ClassPath)下所有的类库。由sun.misc.Launcher$AppClassLoader实现 ,是ClassLoader类中的getSystemClassLoader()方法的返回值。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

    双亲委派模型

    双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这 个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载 请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求 (它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

    源码实现:

    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) {//调用父亲(不为BootstrapClassloader)去查缓存
                        c = parent.loadClass(name, false);
                    } else {//父亲为BootstrapClassloader
                        c = findBootstrapClassOrNull(name);//查缓存和加载/lib/rt/下的核心类
                    }
                } 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;
        }
    }
    

    图解如下:

    三.自定义类加载器
    1. 继承ClassLoader抽象类 extends ClassLoader

    2.重写findClass() 方法,读取class文件,并调用defineClass(),override findClass() -> defineClass(byte[] b, int off, int len)

    3.加密(可以对class字节码文件进行加密处理,可选)

    源码实现如下:

    package com.tc.javabase.jvm;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    /**
     * @Classname MyClassLoader
     * @Description 自定义类加载器
     *
     * 1. 继承ClassLoader抽象类 extends ClassLoader
     * 2.重写findClass() 方法,读取class文件,并调用defineClass(),override findClass() -> defineClass(byte[] b, int off, int len)
     * 3.加密(可以对class字节码文件进行加密处理,可选)
     * @Date 2020/7/29 00:17
     * @Created by zhangtianci
     */
    public class MyClassLoader extends ClassLoader{
    
        public static int seed = 0B10110110;
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            File f = new File("/Users/zhangtianci/work/personal-projects/javabase/src/main/java/", name.replace('.', '/').concat(".ztcclass"));
    
            try {
                FileInputStream fis = new FileInputStream(f);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int b = 0;
    
                while ((b=fis.read()) !=0) {
                    baos.write(b ^ seed);
                }
    
                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 {
    
            encFile("com.tc.javabase.jvm.Hello");
    
            ClassLoader l = new MyClassLoader();
            Class clazz = l.loadClass("com.tc.javabase.jvm.Hello");
            Hello h = (Hello)clazz.newInstance();
            h.sayHello();
    
            System.out.println(l.getClass().getClassLoader());
            System.out.println(l.getParent());
        }
    
        private static void encFile(String name) throws Exception {
            File f = new File("/Users/zhangtianci/work/personal-projects/javabase/src/main/java/", name.replace('.', '/').concat(".class"));
            FileInputStream fis = new FileInputStream(f);
            FileOutputStream fos = new FileOutputStream(new File("/Users/zhangtianci/work/personal-projects/javabase/src/main/java/", name.replace(".", "/").concat(".ztcclass")));
            int b = 0;
    
            while((b = fis.read()) != -1) {
                fos.write(b ^ seed);
            }
    
            fis.close();
            fos.close();
        }
    }
    
    四.破坏双亲委派模型

    1.如何破坏双亲委派模型?

    通过重写loadClass()方法。

    2.什么时候破坏过双亲委派模型?

    - JDK1.2之前,自定义classLoader都必须重写loadClass ()方法。(缺陷)
    - ThreadContextClassLoader可以实现基础类调用实现类的代码
    - 热启动/热部署  tomcat 都有自己的模块指定classLoader(可以加载同一类库的不同版本)
    

    破坏双亲委派模型的简单实现:

    /**
     * @Classname MyClassLoader3
     * @Description 自定义类加载机制  打破双亲委派机制(重写classLoad() )
     *
     *  当重新new 一个自定义加载器实例时 可以重新加载目标class文件到内存中 有一个新的目标class对象
     *  *  否则 内存中只会加载一次
     *  *
     *  *  <----重写loadClass()  打破双亲委派机制时----->
     *  *  当同一个自定义加载器实例 想要加载目标class文件到内存两次(调用defineClass()方法) 会报错
     * @Date 2020/7/29 17:27
     * @Created by zhangtianci
     */
    public class MyClassLoader3 extends ClassLoader{
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
    
            File f = new File("/Users/zhangtianci/work/personal-projects/javabase/src/main/java/" + name.replace(".", "/").concat(".class"));
    
            if(!f.exists()) return super.loadClass(name);
    
            try {
    
                InputStream is = new FileInputStream(f);
    
                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return super.loadClass(name);
        }
    }
    
    五.混合模式

    1.解释器: bytecode intepreter

    2.jit:just in-time compiler

    3.混合模式:

    • 混合使用解释器+热点代码编译
    • 起始阶段采用解释执行
    • 热点代码检测 -多次被调用的代码 - 多次被调用的循环 -进行编译
    • -Xmixd :默认为混合模式,启动速度较快,对热点代码实行检测和编译; -Xint:使用纯解释模式,启动很快,执行稍慢 ; -Xcomp:  使用纯编译模式,执行很快,启动稍慢。
  • 相关阅读:
    What is a .Net Assembly?
    Reading Assembly attributes in VB.NET
    Debugging With Visual Studio 2005
    The Rules for GetHashCode
    The article discusses a couple of new features introduced for assemblies and versioning in Visual Studio 2005.
    Getting a copy of a DLL in the GAC
    Modeling SingleNeuron Dynamics and Computations: A Balance of Detail and Abstraction
    从数学到密码学(八)
    从数学到密码学(十)
    从数学到密码学(三)
  • 原文地址:https://www.cnblogs.com/tc971121/p/13442716.html
Copyright © 2011-2022 走看看