• JVM学习(2):类加载器


    什么是类加载器:

    public class ClassInit {
        public static void main(String[] args) {
            ClassLoader c=ClassInit.class.getClassLoader();
        }
    }

    上面这个ClassLoader就是类加载器

    打印c,注意到一个Launcher类:

    sun.misc.Launcher$AppClassLoader@18b4aac2

    进入Launcher类,注意到其中的两段代码:

    var1 = Launcher.ExtClassLoader.getExtClassLoader();
    this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);

    其中的ExtClassLoader和AppClassLoader有什么关系呢?

    修改成如下代码:

    public class ClassInit {
        public static void main(String[] args) {
            ClassLoader c = ClassInit.class.getClassLoader();
            while (c != null) {
                System.out.println(c);
                c = c.getParent();
            }
        }
    }

    打印:

    sun.misc.Launcher$AppClassLoader@18b4aac2
    sun.misc.Launcher$ExtClassLoader@1540e19d

     于是得到了结论:AppClassLoader有一个父亲是ExtClassLoader

    继续分析ClassLoader,注意到一段代码:

        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)

    独占锁保证了同时只能加载一个类

    然后检查这个类是否被加载过,如果被加载过直接返回;

    如果没有被加载过,那么就递归加载他的父亲,直到没有父亲,最后执行findBootstrapClassOrNull()

    往下看发现这样一段代码:

        // return null if not found
        private native Class<?> findBootstrapClass(String name);

    这个函数没有函数体,被native修饰,代表调用一个本地方法

    这里调用本地方法,根据操作系统的不同,调用的本地方法接口不同,然后调用本地方法库

    得出结论:

    一个类的加载顺序是:BootstrapClassLoader--->ExtClassLoader--->AppClassLoader

    一个类的检查顺序是:AppClassLoader--->ExtClassLoader--->BootstrapClassLoader

    为什么加载顺序要从BootstrapClassLoader开始呢?

    写一个新代码,包名和类名与java源码的List一样:

    package java.util;
    
    public class List{
     public static void main(String[] args){
       xxxxxx
     }
    }

    发现无法运行,报错:在类java.util.List中找不到main方法

    无法运行是非常合理的,否则一个黑客岂不是可以轻易植入病毒代码,然后通过自定义类加载器加入到JVM中

    这种机制称为:双亲委任

    目的:安全

    1.父类如果可以加载,那么不允许子类加载

    2.保证一个类只加载一次

    判断两个对象是不是相等,最重要的条件是看是不是一个类加载器:

    写一个代码:

    package org.dreamtech.cl;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class ClassInit {
        //自定义类加载器
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            ClassLoader loader = new ClassLoader() {
                @Override
                public Class<?> loadClass(String name) throws ClassNotFoundException {
                    try {
                        String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                        InputStream is = getClass().getResourceAsStream(fileName);
                        if (is == null) {
                            return super.loadClass(name);
                        }
                        byte[] b = new byte[is.available()];
                        is.read(b);
                        return defineClass(name, b, 0, b.length);
                    } catch (IOException e) {
                        throw new ClassNotFoundException();
                    }
                }
            };
            String className = "org.dreamtech.cl.ClassInit";
            Object o1 = ClassInit.class.getClassLoader().loadClass(className).newInstance();
            Object o2 = loader.loadClass(className).newInstance();
            System.out.println(o1 == o2);
            System.out.println(o1.equals(o2));
            System.out.println(o1 instanceof org.dreamtech.cl.ClassInit);
            System.out.println(o2 instanceof org.dreamtech.cl.ClassInit);
            System.out.println(o1.getClass().getClassLoader());
            System.out.println(o2.getClass().getClassLoader());
        }
    }

    打印如下:

    false
    false
    true
    false
    sun.misc.Launcher$AppClassLoader@18b4aac2
    org.dreamtech.cl.ClassInit$1@14ae5a5
  • 相关阅读:
    使用Spring MVC统一异常处理实战(转载)
    java 异常
    Java 接口和内部类
    JAVA 继承
    Linux中profile(转载)
    java 对象与类
    Java基本的程序结构设计 数组
    Python虚拟机函数机制之位置参数(四)
    Python虚拟机函数机制之参数类别(三)
    Python虚拟机函数机制之名字空间(二)
  • 原文地址:https://www.cnblogs.com/xuyiqing/p/12468440.html
Copyright © 2020-2023  润新知