The Class Loader Architecture(类装载器体系结构解析)

简介:

这篇文章是从《Inside Java Virtual Machine(深入java虚拟机)》这本书中摘录的,里面对这一章节主要讲的是java类装载器的体系结构,讲的挺好的。

In Java's sandbox, the class loader architecture is the first line of defense. It is the class loader, after all, that brings code into the Java virtual machine--code that could be hostile or buggy. The class loader architecture contributes to Java's sandbox in three ways:

  1. it prevents malicious code from interfering with benevolent code,
  2. it guards the borders of the trusted class libraries, and
  3. it places code into categories (called protection domains) that will determine which actions the code will be allowed to take.

The class loader architecture prevents malicious code from interfering with benevolent code by providing separate name-spaces for classes loaded by different class loaders. A name-space is a set of unique names -- one name for each loaded class -- that the Java virtual machine maintains for each class loader. Once a Java virtual machine has loaded a class named Volcano into a particular name-space, for example, it is impossible to load a different class named Volcano into that same name-space. You can load multiple Volcano classes into a Java virtual machine, however, because you can create multiple name-spaces inside a Java application by creating multiple class loaders. If you create three separate name-spaces (one for each of three class loaders) in a running Java application, then, by loading one Volcano class into each name-space, your program could load three different Volcano classes into your application.

Name-spaces contribute to security because you can in effect place a shield between classes loaded into different name-spaces. Inside the Java virtual machine, classes in the same name-space can interact with one another directly. Classes in different name-spaces, however, can't even detect each other's presence unless you explicitly provide a mechanism that allows them to interact. If a malicious class, once loaded, had guaranteed access to every other class currently loaded by the virtual machine, that class could potentially learn things it shouldn't know or interfere with the proper execution of your program.

Figure 3-1 shows the name-spaces associated with two class loaders, both of which have loaded a type named Volcano. Each name in a name space is associated with the type data in the method area that defines the type with that name. Figure 3-1 shows arrows from the names in the name- spaces to the types in the method area that define the type. The class loader on the left, which is shown dark gray, has loaded the two dark gray types named Climber and Volcano. They class loader on the right, which is shown light gray, has loaded the two light gray types named BakingSoda and Volcano. Because of the nature of name-spaces, when the Climber class mentions the Volcano class, it refers to the dark gray Volcano, the Volcano loaded in the same name space. It has no way to know that the other Volcano, which is sitting in the same virtual machine, even exists. For the details on how the class loader architecture achieves its separation of namespaces, see Chapter 8, "The Linking Model."

 

The class loader architecture guards the borders of the trusted class libraries by making it possible for trusted packages to be loaded with different class loaders than untrusted packages. Although you can grant special access privileges between types belonging to the same package by giving members protected or package access, this special access is granted to members of the same package at runtime only if they were loaded by the same class loader.

Often, a user-defined class loader relies on other class loaders--at the very least, upon the class loaders created at virtual machine startup--to help it fulfill some of the class load requests that come its way. Prior to 1.2, class loaders had to explicitly ask for the help of other class loaders. A class loader could ask another user-defined class loader to load a class by invoking loadClass() on a reference to that user-defined class loader. Or, a class loader could ask the bootstrap class loader to attempt to load a class by invoking findSystemClass(), a static method defined in class ClassLoader. In version 1.2, the process by which one class loader asks another class loader to try and load a type was formalized into a parent-delegation model. Starting with 1.2, each class loader except the bootstrap class loader has a "parent" class loader. Before a particular class loader attempts to load a type in its custom way, it by default "delegates" the job to its parent -- it asks its parent to try and load the type. The parent, in turn, asks its parent to try and load the type. The delegation process continues all the way up to the bootstrap class loader, which is in general the last class loader in the delegation chain. If a class loader's parent class loader is able to load a type, the class loader returns that type. Else, the class loader attempts to load the type itself.

In most Java virtual machine implementations prior to 1.2, the built-in class loader (which was then called the primordial class loader) was responsible for loading locally available class files. Such class files usually included the class files that made up the Java application being executed plus any libraries needed by the application, including the class files of the Java API. Although the manner in which the class files for requested types were located was implementation specific, many implementations searched directories and JAR files in an order specified by a class path.

In 1.2, the job of loading locally available class files was parceled out to multiple class loaders. The built-in class loader, previously called the primordial class loader, was renamed the "bootstrap" class loader to indicate that it was now responsible for loading only the class files of the core Java API. The name bootstrap class loader comes from the idea that the class files of the core Java API are the class files required to "bootstrap" the Java virtual machine.

Responsibility for loading other class files, such as the class files for the application being executed, class files for installed or downloaded standard extensions, class files for libraries discovered in the class path, and so on, was given in 1.2 to user-defined class loaders. When a 1.2 Java virtual machine starts execution, therefore, it creates at least one and probably more user-defined class loaders before the application even starts. All of these class loaders are connected in one chain of parent-child relationships. At the top of the chain is the bootstrap class loader. At the bottom of the chain is what came in 1.2 to be called the "system class loader." Prior to 1.2, the name "system class loader" was sometimes used to refer to the built-in class loader, which was also called the primordial class loader. In 1.2, the name system class loader was more formally defined to mean the default delegation parent for new user-defined class loaders created by a Java application. This default delegation parent is usually going to be the user-defined class loader that loaded the initial class of the application, but may be any user-defined class loader decided upon by the designers of the Java Platform implementation.

For example, imagine you write a Java application that installs a class loader whose particular manner of loading class files is by downloading them across a network. Imagine you run this application on a virtual machine that instantiates two user-defined class loaders on startup: an "installed extensions" class loader and a "class path" class loader. These class loaders are connected in a parent-child relationship chain along with the bootstrap class loader as shown in Figure 3-2. The class path's class loader's parent is the installed extensions class loader, whose parent is the bootstrap class loader. As shown in Figure 3-2, the class path class loader is designated as the system class loader, the default delegation parent for new user-defined class loaders instantiated by the application. Assume that when you application instantiates its network class loader, it specifies the system class loader as its parent. 

 

Imagine that during the course of running the Java application, a request is made of your class loader to load a class named Volcano. Your class loader would first ask its parent, the class path class loader, to find and load the class. The class path class loader, in turn, would make the same request of its parent, the installed extensions class loader. This class loader, would also first delegate the request to its parent, the bootstrap class loader. Assuming that class Volcano is not a part of the Java API, an installed extension, or on the class path, all of these class loaders would return without supplying a loaded class named Volcano. When the class path class loader responds that neither it nor any of its parents can load the class, your class loader could then attempt to load the Volcano class in its custom manner, by downloading it across the network. Assuming your class loader was able to download class Volcano, that Volcano class could then play a role in the application's future course of execution.

To continue with the same example, assume that at some time later a method of class Volcano is invoked for the first time, and that method references class java.util.HashMap from the Java API. Because it is the first time the reference was used by the running program, the virtual machine asks your class loader (the one that loaded Volcano) to load java.util.HashMap. As before, your class loader first passes the request to its parent class loader, and the request gets delegated all the way up to the bootstrap class loader. But in this case, the bootstrap class loader is able to return a java.util.Hashmap class back to your class loader. Since the bootstrap class loader was able to find the class, the installed extensions class loader doesn't attempt to look for the type in the installed extensions. The class path class loader doesn't attempt to look for the type on the class path. And your class loader doesn't attempt to download it across the network. All of these class loaders merely return the java.util.HashMap class returned by the bootstrap class loader. From that point forward, the virtual machine uses that java.util.HashMap class whenever class Volcano references a class named java.util.HashMap.

Given this background into how class loaders work, you are ready to look at how class loaders can be used to protect trusted libraries. The class loader architecture guards the borders of the trusted class libraries by preventing untrusted classes from pretending to be trusted. If a malicious class could successfully trick the Java virtual machine into believing it was a trusted class from the Java API, that malicious class could potentially break through the sandbox barrier. By preventing untrusted classes from impersonating trusted classes, the class loader architecture blocks one potential approach to compromising the security of the Java runtime.

Given the parent-delegation model, the bootstrap class loader is able to attempt to load types before the standard extensions class loader, which is able to attempt to load types before the class path class loader, which is able to attempt to load types before your network class loader. Thus, given the manner in which the parent-child delegation chain is built, the most trusted library, the core Java API, is checked first for each type. After that, the standard extensions are checked. After that, local class files that are sitting on the class path are checked. So if some mobile code loaded by your network class loader wants to download a type across the network with the same name as something in the Java API, such as java.lang.Integer, it won't be able to do it. If a class file for java.lang.Integer exists in the Java API, it will be loaded by the bootstrap class loader. The network class loader will not attempt to download and define a class named java.lang.Integer. It will just use the type returned by its parent, the one loaded by the bootstrap class loader. In this way, the class loader architecture prevents untrusted code from replacing trusted classes with their own versions.

But what if the mobile code, rather than trying to replace a trusted type, wants to insert a brand new type into a trusted package? Imagine what would happen if your network class loader from the previous example was requested to load a class named java.lang.Virus. As before, this request would first be delegated all the way up the parent-child chain to the bootstrap class loader. Although the bootstrap class loader is responsible for loading the class files of the core Java API, which includes a package named, java.lang, it is unable to find a member of the java.lang package with the name, Virus. Assuming this class was also not found among the installed extensions or on the local class path, your class loader would proceed to attempt to download the type across the network.

Assume your class loader is successful in the download attempt and defines the type named java.lang.Virus. Java allows classes in the same package to grant each other special access privileges that aren't granted to classes outside the package. So, since your class loader has loaded a class that by its name (java.lang.Virus) brazenly declares itself to be part of the Java API, you might expect it could gain special access to the trusted classes of java.lang and could possibly use that special access for devious purposes. The class loader mechanism thwarts this code from gaining special access to the trusted types in the java.lang package, because the Java virtual machine only grants that special package access between types loaded into the same package by the same class loader. Since the trusted class files of the Java API's java.lang package were loaded by the bootstrap class loader, and the malicious java.lang.Virus class was loaded by your network class loader, these types do not belong to the same runtime package. The term runtime package, which first appeared in the second edition of the Java Virtual Machine Specification, refers to a set of types that belong to the same package and that were all loaded by the same class loader. Before allowing access to package- visible members (members declared with protected or package access) between two types, the virtual machine makes sure not only that the two types belong to the same package, but that they belong to the same runtime package -- that they were loaded by the same class loader. Thus, because java.lang.Virus and the members of java.lang from the core Java API don't belong to the same runtime package, java.lang.Virus can't access the package-visible members and types of the Java API's java.lang package.

This concept of a runtime package is one motivation for using different class loaders to load different kinds of classes. The bootstrap class loader loads the class files of the core Java API. These class files are the most trusted. An installed extensions class loader loads class files from any installed extensions. Installed extensions are quite trusted, but need not be trusted to the extent that they are allowed to gain access to package-visible members of the Java API by simply inserting new types into those packages. Because installed extensions are loaded with a different class loader than the core API, they can't. Likewise, code found on the class path by the class path class loader can't gain access to package-visible members of the installed extensions or the Java API.

Another way class loaders can be used to protect the borders of trusted class libraries is by simply disallowing the loading of certain forbidden types. For example, you may have installed some packages that contain classes you want your application to be able to load through your network class loader's parent, the class path class loader, but not through your own network class loader. Assume you have created a package named absolutepower and installed it somewhere on the local class path, where it is accessible by the class path class loader. Assume also that you don't want classes loaded by your class loader to be able to load any class from the absolutepower package. In this case, you would write your class loader such that the very first thing it does is make sure the requested class doesn't declare itself as a member of the absolutepower package. If such a class is requested, your class loader, rather than passing the class name to its parent class loader, would throw a security exception.

The only way a class loader can know whether or not a class is from a forbidden package, such as absolutepower, is by the class's name. Thus a class loader must have a list of the names of forbidden packages. Because the name of class absolutepower.FancyClassLoader indicates it is part of the absolutepower package, and the absolutepower package is on the list of forbidden packages, your class loader should throw a security exception absolutely.

Besides shielding classes in different namespaces and protecting the borders of trusted class libraries, class loaders play one other security role. Class loaders must place each loaded class into a protection domain, which defines what permissions the code is going to be given as it runs. More information about this vitally important security job of class loaders will be given later in this chapter. 

 

 本文转自xwdreamer博客园博客,原文链接:http://www.cnblogs.com/xwdreamer/archive/2011/12/05/2296916.html,如需转载请自行联系原作者

 

目录
相关文章
|
7月前
|
Java 开发者
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
241 0
|
8月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
10月前
|
数据可视化 数据挖掘 BI
团队管理者必读:高效看板类协同软件的功能解析
在现代职场中,团队协作的效率直接影响项目成败。看板类协同软件通过可视化界面,帮助团队清晰规划任务、追踪进度,提高协作效率。本文介绍看板类软件的优势,并推荐五款优质工具:板栗看板、Trello、Monday.com、ClickUp 和 Asana,助力团队实现高效管理。
195 2
|
12月前
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
229 2
|
7月前
|
存储 监控 安全
重学Java基础篇—类的生命周期深度解析
本文全面解析了Java类的生命周期,涵盖加载、验证、准备、解析、初始化、使用及卸载七个关键阶段。通过分阶段执行机制详解(如加载阶段的触发条件与技术实现),结合方法调用机制、内存回收保护等使用阶段特性,以及卸载条件和特殊场景处理,帮助开发者深入理解JVM运作原理。同时,文章探讨了性能优化建议、典型异常处理及新一代JVM特性(如元空间与模块化系统)。总结中强调安全优先、延迟加载与动态扩展的设计思想,并提供开发建议与进阶方向,助力解决性能调优、内存泄漏排查及框架设计等问题。
270 5
|
7月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
195 1
|
7月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
257 5
|
8月前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
823 12
|
7月前
|
机器学习/深度学习 人工智能 监控
鸿蒙赋能智慧物流:AI类目标签技术深度解析与实践
在数字化浪潮下,物流行业面临变革,传统模式的局限性凸显。AI技术为物流转型升级注入动力。本文聚焦HarmonyOS NEXT API 12及以上版本,探讨如何利用AI类目标签技术提升智慧物流效率、准确性和成本控制。通过高效数据处理、实时监控和动态调整,AI技术显著优于传统方式。鸿蒙系统的分布式软总线技术和隐私保护机制为智慧物流提供了坚实基础。从仓储管理到运输监控再到配送优化,AI类目标签技术助力物流全流程智能化,提高客户满意度并降低成本。开发者可借助深度学习框架和鸿蒙系统特性,开发创新应用,推动物流行业智能化升级。
207 1
|
8月前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。

热门文章

最新文章

推荐镜像

更多
  • DNS