【JVM】JVM类加载机制

一、先看看编写出的代码的执行过程:

二、研究类加载机制的意义

从上图可以看出,类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行。

研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。

三、类加载的一般过程

原理:双亲委托模式

1、寻找jre目录,寻找jvm.dll,并初始化JVM;

2、产生一个Bootstrap Loader(启动类加载器);

3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。

4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。

5、最后由AppClass Loader加载HelloWorld类。

四、类加载器的特点

1、运行一个程序时,总是由AppClass Loader(系统类加载器)开始加载指定的类。

2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。 
3、Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null.

欢迎加入Java高级架构学习交流群:375989619
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导 进群修改群备注:开发年限-地区-经验 方便架构师解答问题
免费领取架构师全套视频!!!!!!!!

五、类加载器的获取

很容易,看下面例子

public  class HelloWorld {  
         public  static  void main(String[] args) {  
                 HelloWorld hello =  new HelloWorld();  
                 Class c = hello.getClass();  
                 ClassLoader loader = c.getClassLoader();  
                 System.out.println(loader);  
                 System.out.println(loader.getParent());  
                 System.out.println(loader.getParent().getParent());  
         }  
}

打印结果:

sun.misc.Launcher$AppClassLoader@19821f  
sun.misc.Launcher$ExtClassLoader@addbf1  
null  

从上面的结果可以看出,并没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(启动类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。

六、类的加载

类加载有三种方式:

1、命令行启动应用时候由JVM初始化加载

2、通过Class.forName()方法动态加载

3、通过Class.getClassLoader().loadClass()方法动态加载

三种方式区别比较大,看个例子就明白了:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package zhongqiu.common.base;

public class ClassLoadDemo {

    static {

        System.out.println("ClassLoadDemo静态初始化块执行了!");

    }

    public static void main(String[] args) throws ClassNotFoundException {

        ClassLoader loader2 = ClassLoadDemo.class.getClassLoader();

        System.out.println(loader2);

        // 使用ClassLoader.loadClass()来加载类,不会执行初始化块

        // loader2.loadClass("zhongqiu.test.Test");

        // 使用Class.forName()来加载类,默认会执行初始化块

        // Class.forName("zhongqiu.test.Test");

        // 使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块

        Class.forName("zhongqiu.test.Test"false, loader2);

    }

}

七、自定义ClassLoader

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package zhongqiu.common.base.classload;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLClassLoader;

public class MyClassLoader {

    @SuppressWarnings("resource")

    public static void main(String[] args)

            throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException {

        URL url = new URL("file:/D:/javaworkspace/JavaCommon/src/");

        ClassLoader myloader = new URLClassLoader(new URL[] { url });

        Class c = myloader.loadClass("zhongqiu.common.base.classload.Test");

        Test t3 = (Test) c.newInstance();

    }

}

Java.lang包里有个ClassLoader类,ClassLoader 的基本目标是对类的请求提供服务,按需动态装载类和资
源,只有当一个类要使用(使用new 关键字来实例化一个类)的时候,类加载器才会加载这个类并初始化。
一个Java应用程序可以使用不同类型的类加载器。例如Web Application Server中,Servlet的加载使用开发
商自定义的类加载器, java.lang.String在使用JVM系统加载器,Bootstrap Class Loader,开发商定义的其他类
则由AppClassLoader加载。在JVM里由类名和类加载器区别不同的Java类型。因此,JVM允许我们使用不同
的加载器加载相同namespace的java类,而实际上这些相同namespace的java类可以是完全不同的类。这种
机制可以保证JDK自带的java.lang.String是唯一的。

八、

为什么要使用这种双亲委托模式呢?

  1. 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
  2. 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。

作者:leon66666

出处:http://www.cnblogs.com/wangzhongqiu/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

猜你喜欢

转载自blog.csdn.net/J_java1/article/details/82225016