Jvm

类加载器

释放双眼,带上耳机,听听看~!

在上一篇类加载中我们介绍了一个类要加载到内存中要分为7个步骤。其中第一步就是加载也就是通过类的全限定名来获取类的二进制字节流。在Java中把上述加载的过程定义了一个模块叫做类加载器,目的是可以让用户自己决定如何加载一个类。类加载器虽然只是实现类的加载动作,但它在Java中起到的作用却远远要比类加载的功能要重要的多。原因就是类加载器在加载的过程中,会有一些特殊的特性来保证Java的运行安全。例如,每一个类加载器,都有一个独立的类名称空间。说白点就是如果要比较两个类是否相等,必须有一个前提,就是这两个类必须是同一个类加载器加载的,否则,即使比较的是同一个类,如果它们是由不同的类加载器加载的,那么这两个类也是不相等的。除了上述特性外,还有一个非常重要的特性就是双亲委派模式。在介绍双亲委派模式之前我们先看一下在虚拟机中一共都有哪些类加载器。

在虚拟机中其实类加载器有很多种,但主要分为下面的几种,它们分别是:

  • 启动类加载器

启动类加载器主要的功能是加载JAVA_HOME/lib目录中的所有类库。但它加载时有一个前提条件。就是虚拟机会按照指定的文件名来识别加载,例如rt.jar。如果名字不符合的类即使放到lib目录中启动类加载器也不会加载。启动类加载器无法被Java程序直接引用,原因是启动类加载器是用C++语言实现的。

  • 扩展类加载器

扩展类加载器的功能是加载JAVA_HOME/lib/ext目录下的所有类库。由于扩展类加载器是用Java语言本身实现的,所以用户可以直接使用扩展类加载器。

  • 应用程序类加载器

应用程序类加载器的功能是加载用户类路径(ClassPath)上所指定的类库。用户也可以直接使用应用程序类加载器。通过下面的方式即可获取一个应用程序类加载器。

ClassLoader.getSystemClassLoader();

如果程序中没有自定义类加载器,那么应用程序类加载器就为该程序的默认类加载器。

  • 自定义类加载器

自定义类加载器顾名思义就是用户自己开发的类加载器,Java API中提供了一些API可以帮助我们开发出自己的类加载器。

  • 双亲委派模型

在上面我们提到了,在类加载器中双亲委派模型是非常重要的,那么到底什么是双亲委派模型呢?其实在上述这些类加载器中类加载器与类加载器之间都具有某种层次关系,这种关系就叫双亲委派模型。我们先看一下下面的图,然后在做具体说明。

在类加载器中规定除了最顶层的启动类加载器外,其它的所有类加载器都必须有自己的父类加载器。如图中显示,自定义类加载器的父类加载器就是应用程序类加载器,应用程序类加载的器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器。那这么做的目的是什么呢?为什么要有这种层次关系呢?

下面我们看一下双亲委派模型的工作流程。如果一个类加载器收到了一个类的加载请求,首先它不会去加载这个类,而是把这个请求委派给父类加载器,并且每一个类加载器都是如此,所以无论加载的哪一个类最终一定是会委派给启动类加载器加载的,因为上述的委派规则。只有父类加载器无法加载时(该类加载器在它自己的范围内没有搜索到该类)子类加载器才会尝试自己去加载这个类。虽然我们知道了上述类加载器的工作流程,也就是双亲委派模型,那它到底对Java应用程序到底有什么重要的性呢?下面我们举一个简单的例子,来说明一下双亲委派模式的好处。

在有双亲委派模式下,启动类加载器可以抢在扩展类加载器之前去加载类。扩展类加载器可以在应用程序类加载器之前加载类。应用程序类加载器可以在自定义类加载器之前加载类。这样在双亲-孩子委派链中,启动类加载器是最可信任的-核心Java API它会检查每一个被加载的类,然后依次到扩展类加载器、应用程序类加载器、自定义类加载器。如果假如自定义类加载器试图加载一个java.lang.Virus(病毒类)时会怎么样呢?

按照双亲委派模型。这个类会一直委派到启动类加载器来加载这个类 ,因为启动类加载器是核心的Java API 。在java.lang这个包中,没有这个类,所以不能加载。按照双亲委派模型当父类加载器不能加载时,子类加载器尝试加载。依次类推,其它的类加载器也不能加载这个类。于是由用户自定义的类加载器来加载这个类。如果自定义类加载器成功加载了这个类。因为Java允许同一个包中有彼此访问的权限,所以这个java.lang.Virus这个类就允许访问java.lang包下所有类的权限,并且可以利用这个特殊的访问权限来做一些不可告人的目的。这显示是非常不安全的操作。那么类加载器怎么保证Java程序的运行安全呢?

类加载器除了有上述的一些特性外,还有其它的特性来保证,防止这个代码访问java.lang包中的其它类的访问权限。Java虚拟机是怎么实现的呢?

因为在Java虚拟机中只把彼此访问的特殊权限授予同一个类加载器加载到同一个包中的类型。因此 java.lang包的中的类是由启动类加载器加载的,而java.lang.Virus是由自定义类加载器加载的。所以这些类型不属于同一个运行时包。

运行时包:它指由同一个类加载器加载的,属于同一个包的,多个类型集合。

Java虚拟机在允许两个类属于同一个包进行访问之前,Java虚拟机不但要确定它们属于同一个包,还必须确认它们属于同一个运行时包(必须由同一个类加载器加载)。所以java.lang.Virus类不能访问java.lang包下的权限。这样就保证了Java程序的运行安全。

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧