0%

Java-JVM核心机制

JVM核心机制


类加载的全过程

类加载机制

JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终形成JVM可以直接使用的Java类型的过程

  • 加载

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与。

  • 链接

    将Java类的二进制代码合并到JVM的运行状态之中的过程

    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为类变量(static变量)分配肉冻并设置类变量初始值的阶段,这些内存将在方法区中进行分配
    • 解析:虚拟机常量池内的符号引用替换为直接引用的过程
  • 初始化

    • 初始化阶段是执行类构造器< clinit >()方法的过程。类构造器< clinit >()方法是由编译器自动收集类中的所有变量的赋值动作和静态语句块(static块)中的语句合并产生的。
    • 当初始化一个类的时候,如果发现其还没有进行过初始化化,则需要先对其父类初始化
    • 虚拟机会保证一个类的< clinit >()方法在多线程环境中被正确加锁和同步
    • 当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化。

代码验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class t {
public static void main(String[] args) throws Exception {
A a = new A();
System.out.println(A.a);
}
}
class A{
public static int a=1;//静态变量,静态域
static {
System.out.println("静态初始化类A");
a=2;
}
public A(){
System.out.println("创建A类的对象");
}
}
执行结果:
静态初始化类A
创建A类的对象
2
JVM01
JVM01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class t {
static{
System.out.println("静态初始化块t");
}
public static void main(String[] args) throws Exception {
System.out.println("main方法");
A a = new A();
System.out.println(A.a);
}
}
class A extends A_Father{
public static int a=1;
static {
System.out.println("静态初始化类A");
a=2;
}
public A(){
System.out.println("创建A类的对象");
}
}
class A_Father{
static{
System.out.println("静态初始化A_Father");
}
public A_Father(){
System.out.println("创建A_Father对象");
}
}
执行结果:
静态初始化块t
main方法
静态初始化A_Father
静态初始化类A
创建A_Father对象
创建A类的对象
2

类的主动引用(一定会发生类的初始化)

  • new 一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
  • 当虚拟机启动,java Hello,则一定会初始化Hello类。也就是先启动main方法所在的类
  • 当初始化一个类时,如果其父类没有被初始化,则先会初始化他的父类

类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化

    ​ 通过子类引用父类的静态变量,不会导致子类初始化

  • 通过数组定义类引用,不会触发此类的初始化

  • 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

深入类加载器

类加载器原理

  • 类加载器的作用

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类折java.lang.Class对象,作为方法区类数据的访问入口

  • 类缓存

    标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象。

java.class.ClassLoader类介绍

作用:java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义一个Java类,即java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等。

相关方法:

1
2
3
4
5
6
getParent()//返回类加载器的父类加载器
loadClass(String name) //加载名称为name的类,返回的结果是java.lang.Class类的实例
findClass(String name) //查找名称为name的类,返回的结果是java.lang.Class类的实例
findLoadedClass(String name) //查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例
defineClass(String name,byte[] b,int off,int len) //把字节数组b中的内容转换成Java类,返回结果是java.lang.Class类的实例。这个方法被声明为final
resolveClass(Class<?> c) //链接指定的Java类

类加载器树状结构、双亲委托(代理)机制

树状结构

  • 引导类加载器(用C语言写的)

    它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path路径下的内容),是用原生代码来实现的,并不继承自java.lang.ClassLoader

  • 扩展类加载器(用Java写的)

    用来加载Java的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类

  • 应用程序类加载器(用Java写的)

    它根据Java应用的类路径(classpath,java.class.path路径)

    一般来说,Java应用的类都是由它来完成加载的

  • 自定义类加载器(用Java写的)

    开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求

JVM02
JVM02

代理模式

  • 代理模式:

    交给其它加载器来加载指定的类

  • 双亲委托机制:

    就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次追溯,直到最高的爷爷辈的,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

    双亲委托机制是为了保证Java核心库的类型安全(这种机制就保证不会出现用户自己能定义java.lang.Object类的情况)

    类加载器除了用于加载类,也是安全的最基本的屏障

  • 双亲委托机制是代理模式的一种

    并不是所有的类加载器都采用双亲委托机制

    tomcat服务器类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。

自定义类加载器(文件、网络、加密)

  • 自定义类加载器的流程:

    首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回;

    委派类加载请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例;

    调用本类加载器的findClass()方法,试图获取对应的字节码,如果获取到,则调用defineClass()导入类型到方法区;如果获取不到对应的字节码或者其它原因失败,返回异常给loadClass(),loadClass()转抛异常,终止加载过程

    注意:被两个类加载器加载的同一个类,JVM不认为是相同的类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    /**
    * 文件类加载器
    */
    public class FileSystemClassLoader extends ClassLoader {
    //com.zephon.test.User
    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
    this.rootDir = rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    Class<?> c = findLoadedClass(name);
    //先查询有没有加载过这个类,如果已经加载,则直接返回加载好的类。如果没有,则加载新的类
    if (c != null) {
    return c;
    } else {
    ClassLoader parent = this.getParent();
    try{
    c = parent.loadClass(name); //委派给父类加载
    } catch (Exception e){
    // e.printStackTrace();
    }


    if (c != null) {
    return c;
    } else {
    byte[] classData = getClassData(name);
    if (classData == null) {
    throw new ClassNotFoundException();
    } else {
    c = defineClass(name, classData, 0, classData.length);
    }
    }
    }
    return c;
    }

    private byte[] getClassData(String classname) {
    String path = rootDir + "/" + classname.replace(".", "/") + ".class";
    InputStream is = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
    is = new FileInputStream(path);
    byte[] buffer = new byte[1024];
    int temp = 0;
    while ((temp = is.read(buffer)) != -1) {
    baos.write(buffer, 0, temp);
    }
    return baos.toByteArray();

    } catch (Exception e) {
    e.printStackTrace();
    return null;
    } finally {
    if (is != null) {
    try {
    is.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    if (baos != null) {
    baos.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    //测试
    public static void main(String[] args) throws ClassNotFoundException {
    FileSystemClassLoader l = new FileSystemClassLoader("./temp");
    Class<?> c = l.loadClass("temp.com.Hello");
    System.out.println(c);

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    /**
    * 网络类加载器
    */
    public class NetClassLoader extends ClassLoader {
    private String rootUrl;
    public NetClassLoader(String rootUrl){
    this.rootUrl = rootUrl;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    Class<?> c = findLoadedClass(name);
    //先查询有没有加载过这个类,如果已经加载,则直接返回加载好的类。如果没有,则加载新的类
    if (c != null) {
    return c;
    } else {
    ClassLoader parent = this.getParent();
    try{
    c = parent.loadClass(name); //委派给父类加载
    } catch (Exception e){
    // e.printStackTrace();
    }


    if (c != null) {
    return c;
    } else {
    byte[] classData = getClassData(name);
    if (classData == null) {
    throw new ClassNotFoundException();
    } else {
    c = defineClass(name, classData, 0, classData.length);
    }
    }
    }
    return c;
    }

    private byte[] getClassData(String classname) {
    String path = rootUrl + "/" + classname.replace(".", "/") + ".class";
    InputStream is = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
    URL url = new URL(path);
    is = url.openStream();
    byte[] buffer = new byte[1024];
    int temp = 0;
    while ((temp = is.read(buffer)) != -1) {
    baos.write(buffer, 0, temp);
    }
    return baos.toByteArray();

    } catch (Exception e) {
    e.printStackTrace();
    return null;
    } finally {
    if (is != null) {
    try {
    is.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    if (baos != null) {
    baos.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45

    /**
    * 加密工具类
    * @author zephon
    */
    public class EncrptUtil {
    //测试
    public static void main(String[] args) {
    encrpt("./temp/temp/com/Hello.class","./temp/temp/com/a.class");
    }

    public static void encrpt(String src,String dest){
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
    fis = new FileInputStream(src);
    fos = new FileOutputStream(dest);

    int temp = -1;
    while((temp=fis.read())!=-1){
    fos.write(temp^0xff);//取反操作
    }

    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if(fis!=null) {
    fis.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    try{
    if(fos!=null){
    fos.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }


    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    /**
    * 加载文件系统中加密后的class字节码的类加载器
    * @author zephon
    */
    public class DecrptClassLoader extends ClassLoader {
    private String rootDir;

    public DecrptClassLoader(String rootDir) {
    this.rootDir = rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    Class<?> c = findLoadedClass(name);
    //先查询有没有加载过这个类,如果已经加载,则直接返回加载好的类。如果没有,则加载新的类
    if (c != null) {
    return c;
    } else {
    ClassLoader parent = this.getParent();
    try{
    c = parent.loadClass(name); //委派给父类加载
    } catch (Exception e){
    // e.printStackTrace();
    }


    if (c != null) {
    return c;
    } else {
    byte[] classData = getClassData(name);
    if (classData == null) {
    throw new ClassNotFoundException();
    } else {
    c = defineClass(name, classData, 0, classData.length);
    }
    }
    }
    return c;
    }

    private byte[] getClassData(String classname) {
    String path = rootDir + "/" + classname.replace(".", "/") + ".class";
    InputStream is = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
    is = new FileInputStream(path);
    //解密
    int temp = -1;
    while ((temp = is.read()) != -1) {
    baos.write(temp ^ 0xff);
    }
    return baos.toByteArray();
    } catch (Exception e) {
    e.printStackTrace();
    return null;
    } finally {
    if (is != null) {
    try {
    is.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    if (baos != null) {
    baos.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

线程上下文类加载器

JVM03
JVM03

服务器类加载原理和OSGI介绍

JVM04
JVM04
JVM05
JVM05