返回顶部

Jvm(day2—class初始化过程)

Class初始化过程

 

  •  加载

  jvm将.class文件以二进制的形式读取到内存(.class文件本身是2进制的,但其文件内容是16进制的字节码),存放在方法区(类的元信息),并在堆区创建Class对象(类的实例,用于封装方法区的数据结构)。

 

    • 双亲委派模型

  约定类加载器的加载机制:根加载器 > 扩展加载器 > 系统加载器 > 自定义加载器,当类加载器收到类加载请求时,总是先委派父类加载器去完成,只有当父类加载器无法加载时,才会尝试自己加载。

  目的:保证了类的全局唯一性

 

    • 如何打破jdk默认的双亲委派机制?
继承ClassLoader,并重写loadClass
public class MyClassLoader extends ClassLoader {

    private String path;

    public MyClassLoader(String classLoaderName) {
        super();
    }

    public MyClassLoader(ClassLoader parent, String classLoaderName) {
        super(parent);
    }

    /**
     * 寻找加载类
     * @param name
     * @return
     */
    @Override
    protected Class<?> findClass(String name) {
        byte[] data = this.loadClassData(name);
        return super.defineClass(name, data, 0, data.length);
    }

    /**
     * 根据地址加载class文件。
     *
     * @param name
     * @return byte数组
     */
    private byte[] loadClassData(String name) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            name = name.replace(".", "\\");
            JarFile jar = new JarFile(this.path);
            is = jar.getInputStream(jar.getEntry(name.replace("\\", "/") + ".class"));
            baos = new ByteArrayOutputStream();
            int ch;
            while (-1 != (ch = is.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    public void setPath(String path) {
        this.path = path;
    }
}
public class MyTest {
    public static void main(String[] args) throws Exception{
        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> urls = classloader.getResources("com/DataManager.class");
        while (urls.hasMoreElements()) {
            String[] jarPath = urls.nextElement().getPath().split("!");
            // 获取文件的路径以及文件名
            String filePath = jarPath[0].replace("file:/","");
            String classPath = jarPath[1].substring(1).replace(".class", "").replace("/", ".");
            MyClassLoader loader = new MyClassLoader(null, "myClassLoader");

            loader.setPath(filePath);
            // 加载类
            Class<?> clazz = loader.loadClass(classPath);
            // 反射创建实体
            Object obj = clazz.getDeclaredConstructor().newInstance();
            Method method = clazz.getMethod("go", String.class);
            System.out.println(method.invoke(obj, "text"));
        }
    }
}
  • 连接

    • 验证:保证.class文件符合jvm要求
    • 准备:为静态变量设置类型的默认值(静态变量存储在方法区,实例变量随对象一起被分配到堆区;若静态变量被final修饰,则直接显示赋值)
    • 解析:将符号引用(仅仅是个临时的常量符号来表示引用)转为直接引用(即指针,如指向Class对象、类变量、类方法的指针)
  • 初始化

  为变量赋正确的初始值

    • 类初始化的时机(主动引用):

 

1. 创建类实例:new、反射、clone、反序列化;
2. 访问静态变量;
3. 访问静态方法;
4. 初始化类的子类

 

  • 使用

  • 卸载

    • 类何时被卸载?
public class Sample {
    public static void main(String[] args) {
        ClassLoader loader = Sample.class.getClassLoader();
        Sample obj = new Sample();
        Class<? extends Sample> objClass = obj.getClass();
        System.out.println(Objects.equals(objClass.getClassLoader(), loader));
    }
}

 

 

posted @ 2022-09-25 23:44  dork-h  阅读(58)  评论(0)    收藏  举报