InsideJVM(1)--类加载器

 

                                                                                                类加载器体系

类加载器是沙箱的第一道防线,毕竟代码都是由它装入jvm中的,其中也包括有危险的代码。它的安全作用有三点:

一 保护善意代码不受恶意代码的干扰
二 保护已验证的类库
三 代码放入有不同的行为限制的各个保护域中

类加载体系通过使用不同的类加载器把类放入不同的名字空间中从而保护善意代码不受恶意代码的干扰。

JVM为每个类加载器维护一个名字空间。例如,如果jvm在某个名字空间中加载了一个称为volcano的类,就不能再在这个名字空间中加载另一个也称为volcano的类,除非你再创建另一个名字空间。也就是说,如果jvm有三个名字空间,你就可以加载三个叫做volcano的类,一个名字空间一个。

jvm中,同一个名字空间中的类是可以直接交互的,但在不同名字空间中的类就不行,除非提供另外的机制。这样,名字空间就起到了一个屏障的作用。

在图3-1中,显示了两个名字空间,各有一个类加载器,各加载了两个类型,两个名字空间都有一个叫做volcano的类型。左边颜色较深的类加载器加载了类型climbervolcano,右边颜色较浅的类加载器加载了类型bakingsodavolcano。图中的箭头表示名字空间中的名字对在方法区(method area)中对应类型定义数据的引用。因为名字空间的屏障作用,当climber引用volcano时,是指的同一个名字空间中的volcano。尽管都在同一个jvm,它也无法知道另一个名字空间中的volcano,。如果想知道如何达到名字空间的分隔的,可以看第八章“链接模式”

类加载器体系可以通过使用不同的类加载器加载可信包(trusted packages)和不可信包(untrusted packages)从而保护可信包的安全。尽管你可以对同一包中的类型制定访问控制,但这种控制只有在被同一个加载器加载的前提下才起作用。

通常,一个用户定义的类加载器需要依赖其他类加载器来完成任务,至少需要一个在jvm启动时创建的类加载器,这个类加载器称为启动类加载器。在1.2版本以前,类加载器必须显示的调用其他类加载器,如调用其他用户定义的类加载器的loadClass方法,或者调用启动类加载器(bootstrap class loader)的静态函数findSystemClass()。
在1.2版本中,一个类加载器要求另一个类加载器加载某个类型的过程被规范化为代理模式(parent-delegation model 译者:就是Chain of Responsibility模式)

在某个类加载器试图以自己的方式加载一个类时,它首先缺省把这个工作交给自己的父对象。而这个父对象又会首先把这个任务交给自己的父对象处理,这样这个任务会一直传到启动类加载器,因为启动类加载器通常是代理链的最后一个类。如果父类加载器能够加载这个类型,就会返回此类型,否则由子类加载器处理。
在1.2版本以前的多数jvm的实现中,内建类加载器负责加载本地可用的类文件,通常包括java应用的类文件和所有这个应用需要的库,尽管加载所需类文件的方式根据应用不同而不同,但许多应用都以class path定义的路径来搜寻所需类文件。

在1.2版本中,加载本地可用类文件的任务被分解给了多个类加载器。以前称为原始类加载器(primordial class loader)的类加载器被改称为启动类加载器,用来表示它只用来加载核心java api的类文件,因为核心java api类文件是用来启动jvm的。

而负责加载其他类文件的任务都给了用户定义的类加载器(译者:这里指广义用户,包括虚拟机的实现者),这些类文件包括应用的类文件,用来进行安装和下载的标准扩展类文件,用来在class path中查找库的类文件等等。
因此当1.2的JVM开始运行时,它会创建至少一个用户定义的类加载器,所有的这些类加载器串成一个链,在链的头部是启动类加载器,在链的尾部是系统类加载器(system class loader)。在1.2之前,有时称内建类加载器为系统类加载器,在1.2,这个名字被更正式的用于称呼java应用所创建的新的类加载器的父亲。

这个缺省父代理通常来加载应用的初始类,但任何用户定义的类加载器都可能被java平台的设计者所改变。
例如,假如你写了一个应用,此应用需要安装一个类加载器,用来加载从网络上下载的类文件。这个应用运行在一个jvm上,而这个jvm有两个用户定义的类加载器,一个是安装扩展类加载器,另一个是类路径类加载器。它们和启动类加载器串成一个链,依次为:启动类加载器,安装扩展类加载器,类路径加载器。

如图3-2,类路径加载器被设计成了系统类加载器,它将是java应用新的类加载器的父亲。当你的应用的网络类加载器被安装时,它将这个系统类加载器设为它的父亲。

假如在java应用运行中需要加载一个称为volcano的类,你的类加载器会首先把这一任务交给它的父亲,类路径类加载器,去查找和加载这个类文件。而类路径类加载器同样首先交给自己的父亲,安装扩展类加载器,去完成任
务。这样,这个任务最后交给启动类加载器去首先尝试处理。

假设类volcano不是java api的一部分,也不是安装扩展和类路径的一部分,所有对应的类加载器都没有返回这个类型,这样就轮到你自己的类加载器了。它将会从网络上下载此类文件,这样这个类就称为你应用中的一部分了。
我们继续这个例子,假如以后某个时候第一次调用了类volcano的一个方法,这个方法中引用了java api中的类java.util.HashMap,而这个类是这个应用第一次引用,这样jvm就要求你的类加载器去加载这个类。象以前一样,这个请求最终到达了启动类加载器。

但这次不同,启动类加载器能够加载java.util.Hashmap并把它返回给了你的类加载器。这样安装扩展类加载器和类路径类加载器只起到了一个传递的作用,而你的类加载器也不用从网络下载这个类文件了。从此,在类volcano中,所有对java.util.Hashmap的引用都会使用这个类。

有了这个背景知识,我们就可以看看类加载器是如何被用来保护可信库(trusted libraries)的了。类加载器体系通过防止不可信类冒充可信类保护了可信类的边界,防止了对java runtime安全的潜在威胁。
有了这个链状的代理关系,我们知道,要加载一个类,需要链上的类加载器按特定顺序逐次检查,这样自己定义的类加载器始终处于一个较低优先级的状态,如果你自己的类加载器想要从网络上下载一个叫做java.lang.Integer的类是不可能的。它只能使用从父类加载器传来的类型。通过这种方法,防止了用不可信代码替换可信代码的发生。

但假如代码不准备去替换一个可信类型,而只想在可信包中插入一个新类型呢?假如在前面例子中,你的网络类加载器想加载一个叫做java.lang.Virus的类。象以前一样,加载类的要求在链内传递,直到启动类加载器,尽管启动类加载器负责加载核心java api的类,其中也包括一个叫java.lang的包名,但找不到Virus,我们假设这个类同样在安装扩展类加载器和类路径类加载器中也没有找到。这样你的网络类加载器就从网络上下载了这个类。
假设你成功下载了类java.lang.VirusJava对在同一个包中的类的相互访问有一定的特权。因此,因为你的类加载器加载了一个无耻的宣称自己是java api的一部分的类java.lang.Virus,你肯定希望能够享受到某种特权,从而干一些罪恶的勾当。但类加载机制制止了这种事情的发生,因为类加载机制限制这种特权只有在同一个类加载器加载的前提下。

因为java apijava.lang包中的可信类都由启动类加载器加载,而邪恶的java.lang.Virus由你的网络类加载器加载,他们并不属于同一个运行包(runtime package)。

运行包这个术语首次在jvm第二版的规范中引入,指由同一个类加载器加载的同一个包中的所以类型。
在允许同一个包中的两个类型访问之前,jvm还有确信此两个类型是由同一个类加载器加载的。
因此,jvm不允许java.lang.Virus去访问java apijava.lang 包中的其他类型,因为他们不是由同一个类加载器加载的。

引入运行包的目的之一就是使用不同的类加载器加载不同类型的类文件。启动类加载器用来加载最值得信赖的核心java api。安装扩展类加载器用来加载安装扩展的任何类文件。虽然安装扩展也是可以信赖的,但还没有到可以向java api添加新类型的程度。同样,类路径类加载器加载的类也不能访问安装扩展和java api中的类型。

类加载器也可以简单的禁止加载某些类型来保护可信代码。

例如,你可能安装了某些包,其中有一些类你想由类路径类加载器来加载,而不是你的网络类加载器。
假设你创建一个叫做absolutepower的包,并把它安装在了类路径类加载器的访问范围内。同时你希望由你的类加载器加载的类不能加载absolutepower包中任何类。这样在你的类加载器中的第一件事就是检查需要加载的类是不是把自己声明为absolutepower中的类,如果是,则抛出一个异常,并不是交给父类加载器来处理。
类加载器机制除了屏蔽不同名字空间,保护可信类库外,还把每个加载的类放到了一个保护域(protection domain)中,保护域对类的活动范围也有一个定义。

 

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
程序员的必经之路! 【限时优惠】 现在下单,还享四重好礼: 1、教学课件免费下载 2、课程案例代码免费下载 3、专属VIP学员群免费答疑 4、下单还送800元编程大礼包 【超实用课程内容】  根据《2019-2020年中国开发者调查报告》显示,超83%的开发者都在使用MySQL数据库。使用量大同时,掌握MySQL早已是运维、DBA的必备技能,甚至部分IT开发岗位也要求对数据库使用和原理有深入的了解和掌握。 学习编程,你可能会犹豫选择 C++ 还是 Java;入门数据科学,你可能会纠结于选择 Python 还是 R;但无论如何, MySQL 都是 IT 从业人员不可或缺的技能!   套餐中一共包含2门MySQL数据库必学的核心课程(共98课时)   课程1:《MySQL数据库从入门到实战应用》   课程2:《高性能MySQL实战课》   【哪些人适合学习这门课程?】  1)平时只接触了语言基础,并未学习任何数据库知识的人;  2)对MySQL掌握程度薄弱的人,课程可以让你更好发挥MySQL最佳性能; 3)想修炼更好的MySQL内功,工作中遇到高并发场景可以游刃有余; 4)被面试官打破沙锅问到底的问题问到怀疑人生的应聘者。 【课程主要讲哪些内容?】 课程一:《MySQL数据库从入门到实战应用》 主要从基础篇,SQL语言篇、MySQL进阶篇三个角度展开讲解,帮助大家更加高效的管理MySQL数据库。 课程二:《高性能MySQL实战课》主要从高可用篇、MySQL8.0新特性篇,性能优化篇,面试篇四个角度展开讲解,帮助大家发挥MySQL的最佳性能的优化方法,掌握如何处理海量业务数据和高并发请求 【你能收获到什么?】  1.基础再提高,针对MySQL核心知识点学透,用对; 2.能力再提高,日常工作中的代码换新貌,不怕问题; 3.面试再加分,巴不得面试官打破沙锅问到底,竞争力MAX。 【课程如何观看?】  1、登录CSDN学院 APP 在我的课程中进行学习; 2、移动端:CSDN 学院APP(注意不是CSDN APP哦)  本课程为录播课,课程永久有效观看时长 【资料开放】 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化。  下载方式:电脑登录课程观看页面,点击右侧课件,可进行课程资料的打包下载。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值