首页 > 编程语言 > 详细

【java】JVM结构和类的加载原理

时间:2015-03-25 06:37:59      阅读:1733      评论:0      收藏:0      [点我收藏+]

  Java语言中,类只有被加载到JVM中才能运行,当运行指定的java程序时,JVM会将编译生成的 .class文件按照一定的规则加载到内存中,并组织成为一个完整的应用程序。类的加载过程是由类加载器完成的(即由ClassLoader和它的子类完成),而类加载器本身也是一个类,其实质是将类文件由硬盘加载到内存中。

  类的加载方式有两种:

(1)显式加载

   通过调用class.forName()方法将所需的类加载到JVM中

(2)隐式加载

   程序在创建新的对象时,隐式地调用类加载器把对应的类加载到JVM中

  在java语言中,类的加载是动态且灵活的,往往一个大的项目包含很多类,而每一个类或接口都对应一个.class文件,当程序运行时只需要将需要的类(保证程序运行的基础类,例如基类)加载到JVM中,暂时不需要的类可以先不加载,这样一方面可以提高运行速度,另一方面也可以降低程序运行时对内存的开销。而且,每一个类文件都可以看成是动态的加载单元,当项目需要对某个类进行修改时,修改完毕后只需要重新编译加载被修改的类即可,而不用全部的类都重新进行编译。

  类可以分为三种:系统类、扩展类、自定义类,而java根据不同的类提供了不同的类加载器

       Bootstrap Loader             <==加载系统类(jre/lib/rt.jar的类)

              ↘

              ExtClassLoader        <==加载扩展类(jar/lib/etc/*.jar的类)

                  ↘

                  AppClassLoader    <==加载应用类(classpath指定的目录或jar中的类)

  具体步骤:

(1)首先java.exe会找到JRE,并且找到位于JRE内部的jvm.dll,这才是真正的java虚拟机,然后加载到动态库,激活java虚拟机。

(2)进行初始化操作,结束之后产生Bootstrap Loader启动类加载器

(3)Bootstrap Loader除了进行一些基本的初始化动作外,最重要的是加载ExtClassLoader扩展类加载器,并且设定其Parent为null,也就代表其父加载器为Bootstrap Loader

(4)然后Bootstrap Loader再要求加载Launcher.java中的AppClassLoader(自定义类加载器),并设定其Parent为ExtClassLoader实体,这两个加载器都是以静态类的形式存在的。

※需要注意的是,其实parent是谁跟被谁加载的并没有直接关系

我们可以测试一下:

package test;

public class classloader {
    public static void main(String[] args)
    throws Exception{
        ClassLoader App = classloader.class.getClassLoader();//class加载器
        System.out.println(App);
        ClassLoader Ext = App.getParent();//上一层加载器
        System.out.println(Ext);
        ClassLoader Boot = Ext.getParent();//根部加载器
        System.out.println(Boot);
    }

}

运行结果:

sun.misc.Launcher$AppClassLoader@39579371
sun.misc.Launcher$ExtClassLoader@2490fd20
null

Bootstrap Loader输出null的原因是它是由C++语言实现的,所以在java语言中看不到

程序说明classloader这个类是由AppClassLoader加载的

  类的加载主要有三步:

(1)装载:根据查找路径找到相应的class文件并导入

(2)链接:检查class文件是否正确-->给类中的静态变量分配存储空间-->将符号引用转换成直接引用

(3)初始化:静态变量和静态代码块的初始化操作

  双亲委托机制:

  双亲委托模式也就是一个类加载器请求另一个类加载器来加载类型的过程。

  除启动类加载器以外的每一个类加载器,都有一个“双亲”类加载器 ,在某个特定的类加载器试图以常用方式加载某个类以前,它会先默认地将这个任务“委派”给它的双亲,请求它的双亲来加载这个类。这个双亲再依次请求它自己的双亲来加载这个类型。这个委派的过程一直向上继续,直到达到启动类加载器,通常启动类加载器是委派链中的最后一个类加载器。如果一个类加载器的双亲类加载器有能力来加载这个类型。则这个类加载器返回这个类型。否则,这个类加载器试图自己来加载这个类。
  当一个程序运行时,虚拟机在启动时实例化了两个用户自定义类加载器:一个“扩展类加载器”,一个“自定义类加载器”.这些类装载器和启动类加载器一起联入一个Parent-Child委托链中,启动类加载器在最顶端。

 

  例如:

package java.lang
public class String    //定义一个和JDK中String一样的类
{     
    public static void main(String[] args)
    {
    
    }
}


  我们定义了一个与JDK中String一模一样的类,包括java.lang都是一样的,只是在我们定义的String类中包含了主函数。

  运行结果:

java.lang.NoSuchMethodError: main
Exception in thread "main"

  出现这样的结果的原因是:

  运行代码时,JVM会首先创建一个自定义类加载器,不妨叫做AppClassLoader,并把这个加载器链接到委托链中:AppClassLoader -> ExtClassLoader -> BootstrapLoader。然后AppClassLoader会将加载java.lang.String的请求委托给ExtClassLoader,而 ExtClassLoader又会委托给最后的启动类加载器BootstrapLoader。启动类加载器BootstrapLoader只能加载JAVA_HOME\jre\lib中的class类(即J2SE API),而标准API中确实有一个java.lang.String,但是这个类并不是我们自己定义的类。而BootstrapLoader以为找到了这个类,便加载了j2se api中的java.lang.String。

  最后出现上面的加载错误,注意这并不是异常,JVM已经退出了,因为API中的String类是没有main方法的。

 

【java】JVM结构和类的加载原理

原文:http://caoyue.blog.51cto.com/9876038/1623974

(0)
(2)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!