java高并发编程--06--类加载器、jvm类加载器、线程上下文加载器

1.类的加载器

1.1类的加载过程:
1)加载阶段,查找并加载二进制文件,即class文件
2)连接阶段,分三步
第一步:验证:验证class文件的正确性
第二部:准备:为类的静态变量分配内存,并为其初始化默认值
第三步:解析:把类中符合引用转换为直接引用
3)初始化阶段:为类的静态变量赋予正确的初始值

1.2类的主动使用和被动使用
接口或类在被首次主动使用时才进行初始化
8种主动使用类的场景:
1)new类的对象
2)访问类的静态变量
3)访问类的静态方法
4)对类进行反射操作
5)初始化类的子类
6)类为启动类
除了上面6种情况,其余的都是被动使用,不会导致类的加载和初始化
1)构造某个类的数组
2)引用类的静态常量(被final+static修饰的)

1.3类的加载过程
运行以下代码

public class Singleton {
    //1
    private static int x = 0;
    private static int y;
    //2
    private static Singleton singleton = new Singleton();
    private Singleton() {
        x ++;
        y ++;
    }
    public static Singleton getInstance() {
        return singleton;
    }
    public static void main(String[] args) {
        Singleton singleton = getInstance();
        System.out.println(singleton.x);
        System.out.println(singleton.y);
    }
}

输出结果:
1
1

将注释2处代码移到注释1处,再次运行输出结果:
0
1

1.3.1类的加载阶段
类的加载就是将class文件中的二进制数据读取到内存中,让后将该字节流所代表的静态存储结构转换为方法区中运行时的数据结构,并且在堆内存中生成一个该类的java.lang.Class对象,作为访问方法区数据结构的入口,如下图所示:

 

 类加载的最终产物就是堆内存中的class对,对于同一个ClassLoader来讲,不管某个类被加载多少次,对应的堆内存中的class对象始终只有一个。

1.3.2类的连接阶段
1)验证
验证文件格式:文件头部魔术因子是否是class文件的,主次版本号(低版本jre不能运行高版本jdk编译的class文件),构成class文件字节流是否完整,常量池中的常量是否有不支持的类型,指向常量中的引用是否指到不存在的常量,其他信息
元数据验证:元数据验证是对class字节流语义分析的过程,会检测是否存在父类、继承接口,父类、接口释放合法与存在,检查类是否继承final类或重写final方法,检测是否是抽象类或实现父类、接口的所有抽象方法,重载释放合法等。
字节码验证:验证程序的控制流程,如循环、分支等
符号引用验证:主要验证符号引用转换为直接引用时的合法性
2)准备
对类变量、即静态变量,分配内存并设置初始值(初始值与变量类型有关,如Int类型的变量初始值是0),注意是静态变量,不包含静态常量,对静态常量的引用不会导致类的初始化。
3)解析
类接口的解析:完成对主动使用的类、接口的加载
字段的解析:解析所访问类或接口中的字段
类方法解析
接口方法解析

1.3.3类的初始化阶段
类初始化阶段是整个类加载过程的最后一个阶段,在初始化阶段主要做的是执行<clinit>()方法,在<clinit>()方法中所有的变量都会被赋予正确的值,即程序编写时指定的值
<clinit>()方法是在编译阶段生成中,已经包含在class文件中,<clinit>()方法中包含所有类变量的赋值动作和静态代码块的执行代码,编译器收集的顺序是执行语句在源文件中出现的顺序(如静态代码中访问其后定义的类变量是行不通的)
<clinit>()方法方法不同于构造函数,它不需要调用父类构造器,虚拟机会保证父类的<clinit>()方法最先执行,因此父类的静态变量总能够得到优先赋值。
<clinit>()方法不一定有,如果某个类没有类变量和静态代码块,可能就没有

 

2.JVM类加载器
任意一个class,都是要由加载它的类加载器和这个类本身来确立其在jvm中的唯一性,这就是运行时包。

2.1jvm内置三大类加载器
jvm提供了3大内置类加载器,不同的类加载器负责将不同的类加载到jvm内存中,他们之间严格遵守父委托机制,如下图所示:

 

 

2.1.1根类加载器(Bootstrap ClassLoader)
Bootstrap类加载器为最顶层加载器,有c++编写,主要负责虚拟机核心类库的加载,如java.lang包的类的加载
2.1.2扩展类加载器(Ext ClassLoader)
扩展类加载器主要用于加载JAVA_HOME下的jre\lb\ext子目录里面的类,纯java实现
2.1.3系统类加载器(Application ClassLoader)
系统类加载器主要负责加载classpath下的类库资源,如项目中引入的第三方jar包,系统类加载器的父加载器是扩展类加载器,系统类加载器是自定义类加载器的默认父加载器。

2.2自定义类加载器
所有自定义类加载器都是ClassLoader的子类(间接子类),ClassLoader是一个抽象类,没有抽象方法,但其内的findClass方法必须重写,否则抛出ClassNotFoundException。

protected Class<?> findClass(String name) throws ClassNotFoundException {
  throw new ClassNotFoundException(name);
}

自定义类加载器:
1)类加载器:

package cp10.cp02;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class MyClassLoader extends ClassLoader {
    //默认class文件存放目录
    private final static Path DEFAULT_CLASS_DIR = Paths.get("C:\\Users\\Administrator\\Desktop\\classesdir");
    private final Path classDir;
    //使用默认class路径
    public MyClassLoader() {
        super();
        this.classDir = DEFAULT_CLASS_DIR;
    }
    //指定class路径
    public MyClassLoader(String classDir){
        this.classDir = Paths.get(classDir);
    }
    //指定class路径和父加载器
    public MyClassLoader(String classDir,ClassLoader parent) {
        super(parent);
        this.classDir = Paths.get(classDir);
    }
    //重写findClass方法
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //读取文件
        byte[] bytes = readClassBytes(name);
        if(bytes == null || bytes.length == 0) 
            throw new ClassNotFoundException("The class " + name +" not Found");
        //调用defineClass定义为Class对象
        Class<?> clazz = this.defineClass(name,bytes, 0, bytes.length);
        return clazz;
    }
    //读取class文件到内存
    private byte[] readClassBytes(String className) throws ClassNotFoundException {
        //将包名.转换为文件分隔符/
        className = className.replaceAll("\\.", "/");
        Path fullClassPath = classDir.resolve(Paths.get(className+".class"));
        if(! fullClassPath.toFile().exists())
            throw new ClassNotFoundException("The class " + className +" not Found");
        try(ByteArrayOutputStream baos = new ByteArrayOutputStream()){
            Files.copy(fullClassPath, baos);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException("The class " + className +" not Found",e);
        }
    }
}
2)测试
package cp10.cp02;

public class Test {
    static {
        System.out.println("--class is load");
    }
    public void test() {
        System.out.println("--Test()");
    }
}

//将Test的class文件复制到前面的默认目录再删掉IDE中这个类

package cp10.cp02;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyClassLoaderTest {
    public static void main(String[] args) 
            throws ClassNotFoundException, NoSuchMethodException, 
            SecurityException, InstantiationException, 
            IllegalAccessException, IllegalArgumentException, 
            InvocationTargetException {
        MyClassLoader loader = new MyClassLoader();
        Class<?> test = loader.loadClass("cp10.cp02.Test");
        Object newInstance = test.newInstance();
        Method method = test.getMethod("test");
        method.invoke(newInstance);
    }
}

输出结果:
--class is load
--Test()

 

posted @ 2020-03-04 15:59  守望一心  阅读(210)  评论(0编辑  收藏  举报