理解Java类加载机制(译文)
理解java类加载机制
你想写类加载器?或者你遇到了ClassCastException异常,或者你遇到了奇怪的LinkageError状态约束异常。应该仔细看看java类的加载处理了。
什么是类加载器以及它是如何对类进行加载的?
一个Java类是由java.lang.ClassLoader类的一个实例加载的。由于java.lang.ClassLoader自己本身是一个抽象类所以一个类加载器只能够是java.lang.ClassLoader类的具体子类的实例。如果是这种情况,那么哪一个类加载器来加载java.lang.ClassLoader这个类?(经典的"谁将会加载加载者"引导的问题)。事实证明JVM有一个内置的引导类加载器。引导加载器加载java.lang.ClassLoader和许多其他java平台类。
要加载一个具体的java类,例如com.acme.Foo,JVM调用java.lang.ClassLoader类的loadClass方法(事实上,JVM查找loadClassInternal方法-如果发现loadClassInternal方法则用loadClassInternal方法,否则JVM使用loadClass方法,而loadClassInternal方法会调用loadClass方法)。loadClass方法接收类名来加载类返回表示加载的类的java.lang.Class实例。事实上loadClass方法找到.class文件(或者URL)的实际字节,并调用defineClass方法来构造出java.lang.Class类的字节数组。加载器上调用loadClass方法的加载器称之为初始化加载器(即,JVM启动加载使用这个加载器).但是,启动加载器不是直接加载类的-而是可能委托给另外一个类加载器(例如,它的父加载器)-它自己也可能委派给另外一个加载器去加载等等。最终在委托链中的某些类加载器对象调用defineClass方法加载有关的类(com.acme.Foo)。
这个特殊的类加载器叫做com.acme.Foo的确切加载器。在运行时,一个java类是由类的完全限定类名和和类加载器确定其唯一性的。如果指定相同的类名(即,相同的完全限定类名)的类是由两个不同的类加载器加载的,那么这些类是不同的-即使这些.class的字节码是相同的并且都是从相同的位置进行加载的(相同的URL)。
有多少种类加载器并且它们是从哪里加载类的?
即便是一个简单的"hell world"java程序,也有至少3种类加载器。
引导类加载器
- 加载java平台基础类(例如java.lang.Object,java.lang.Thread等)。
- 加载rt.jar包种的类($JRE_HOME/lib/rt.jar)。
- -Xbootclasspath参数可以更改启动类路径-Xbootclasspath/p: 和 -Xbootclasspath/a:参数可以前置/追加额外的引导目录-一定要格外小心这样做。在大多数情况下,要避免随意变更启动类路径。
- 在Sun的实现中,只读系统属性sun.boot.class.path设置为指向启动类路径。注意你不能在运行时修改这个属性-如果你修改了修改也不会生效。
- 这个加载器由java的null表示。例如,java.lang.Object.class.getClassLoader()方法将会返回null(还有其他的类例如java.lang.Integer,java.awt.Frame,java.sql.DriverManager等)
扩展类加载器
- 从已安装的可选包中加载类。
- 从$JRE_HOME/lib/ext目录下加载jar文件。
- 可以使用-Djava.ext.dirs命令修改系统属性java.ext.dirs来修改扩展目录。
- 在Sun的实现中,它是sun.misc.Launcher$ExtClassLoader的实例(事实上它是sun.misc.Launcher类的一个内部类)。
- 在编写代码中。你可能读取(只能够读取!)系统通属性java.ext.dirs来寻找哪个目录是扩展目录。你不能在在运行期间修改这个属性-即使你修改了修改也不会生效。
应用类加载器
- 从应用的classpath中加载类。
- 使用环境变量CLASSPATH(者-cp或者-classpath选项设置应用的classpath,如果CLASSPATH和-cp都找不到,则使用"."(当前目录)。
- 只读系统属性java.class.path是应用类路径。你不能在运行期间修改这个属性-即使你修改了修改也不会生效。
- java.lang.ClassLoader.getSystemClassLoader()方法的返回值是这个加载器
- 这个加载器也叫做"系统加载器"-不过不要将加载java"系统"类的启动加载器混淆。
- 这个加载器加载你应用的"main"(由main方法的类)。在Sun的实现中,它是sun.misc.Launcher$AppClassLoader的一个实例(事实上它是sun.misc.Launcher类的一个内部类)。
- 默认的应用加载器用扩展加载器做为它的父加载器。
- 你可以使用-Djava.system.class.loader命令更改应用的类加载器。这个值指定java.lang.ClassLoader的子类的名字.首先默认应用加载器加载已命名的类(这个类在CLASSPATH或者-cp)和创建一个它的实例.新创建的这个类的实例用于加载应用的main类。
一个类典型的类记载流程
让我们假设你正在运行一个"hello world" java程序。我们来看一下类的加载流程。JVM用应用类加载器加载主方法(main)所在的类。如果你运行下面的程序
class Main {
public static void main(String[] args) {
System.out.println(Main.class.getClassLoader());
javax.swing.JFrame f = new javax.swing.JFrame();
f.setVisible(true);
SomeAppClass s = new SomeAppClass();
}
它会打印如下内容
sun.misc.Launcher$AppClassLoader@17943a4
每当一些其它的类引用在Main类中被解析时,JVM用Main所在类的明确的加载器-应用类加载器-做为初始化加载器。在上面的列子中,为了加载javax.swing.JFrame类JVM将使用应用类加载器做为一个初始化加载器。即,JVM将用应用类应用做为初始化加载器。即。JVM将调用loadClass()方法(loadClassInternal方法)在应用类加载器中。应用类加载器委托给扩展类加载器。
扩展加载器检查这是否是一个启动类(用私有方法 - ClassLoader.findBootstrapClass),启动类加载器是否从rt.jar加载过它。
当SomeAppClass的引用类被解析时,JVM有着相同的过程-用应用类加载器做为初始化加载器。
应用加载器委托给扩展加载器,扩展加载器检查启动加载器,启动加载器找不到"SomeAppClass"类,
于是扩展加载器检查"SomeAppClass"类是否在扩展jars里,结果发现不在。
于是应用类加载器检查在应用的CLASSPATH下的.class字节,如果找到了则进行加载,如果没有找到,将会抛出NoClassDefFoundError异常。
总结:
Class是由具体的类加载器与类的完全限定类名唯一定义的。
如果具体的类加载器不同,即使.class字符是从文件系统中的相同位置进行加载的Classes也是不同的。
类加载器委托给父加载器进行加载。
加载Bar类中引用的Foo类,JVM使用Bar类的确切的类加载器做为初始化加载器。JVM会在Bar类的确切加载器上会调用loadClass()方法加载Foo类。
JVM缓存->运行时的类每次初始化加载都将被记录。JVM将会缓存用于以后的解析。即,loadClass()方法不会对于每一次引用都调用。这能确保时间的不变性-即,一个类加载器不允许加载相同类名但字节码不同的类。
他是由缓存来实现的。好的类加载器应该通过调用ClassLoader得call()方法来检查缓存。
原文链接
Understanding Java class loading https://blogs.oracle.com/sundararajan/entry/understanding_java_class_loading
理解Java类加载机制(译文)的更多相关文章
- 深入理解Java类加载机制,再也不用死记硬背了
谈谈"会"的三个层次 在<说透分布式事务>中,我举例里说明了会与会的差别.对一门语言的学习,这里谈谈我理解的"会"的三个层次: 第一层:了解这门语言 ...
- 阿里面试题,深入理解Java类加载机制
类的生命周期 包括以下 7 个阶段: 加载(Loading) 验证(Verification) 准备(Preparation) 解析(Resolution) 初始化(Initialization) 使 ...
- 深入理解Java类加载
本文目的: 深入理解Java类加载机制; 理解各个类加载器特别是线程上下文加载器; Java虚拟机类加载机制 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最 ...
- Java类加载机制的理解
算上大学,尽管接触Java已经有4年时间并对基本的API算得上熟练应用,但是依旧觉得自己对于Java的特性依然是一知半解.要成为优秀的Java开发人员,需要深入了解Java平台的工作方式,其中类加载机 ...
- 深入理解和探究Java类加载机制
深入理解和探究Java类加载机制---- 1.java.lang.ClassLoader类介绍 java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字 ...
- Java类加载机制深度分析
转自:http://my.oschina.net/xianggao/blog/70826 参考:http://www.ibm.com/developerworks/cn/java/j-lo-class ...
- 深入理解Java类加载器(ClassLoader)
深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...
- 两道面试题,带你解析Java类加载机制
文章首发于[博客园-陈树义],点击跳转到原文<两道面试题,带你解析Java类加载机制> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Gr ...
- 【转】两道面试题,带你解析Java类加载机制(类初始化方法 和 对象初始化方法)
本文转自 https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html 关键语句 我们只知道有一个构造方法,但实际上Ja ...
随机推荐
- JavaScript八张思维导图—基本语句
JS基本概念 JS操作符 JS基本语句 JS数组用法 Date用法 JS字符串用法 JS编程风格 JS编程实践 不知不觉做前端已经五年多了,无论是从最初的jQuery还是现在火热的Angular,Vu ...
- 第三方推送 JPush 配置中的引入so库问题
Gradle入门:http://www.infoq.com/cn/articles/android-in-depth-gradle/ 当所需要的so库已经复制到libs目录下,系统还是提示 找不到so ...
- 主题:Windows系统服务器磁盘挂载
可能很多人发现VPS重装之后D盘.E盘不见了,其实并没有不见只是磁盘未挂载,下面由小编来为大家讲讲如何挂载磁盘 远程登录服务器后,桌面上只有一个回收站的,我们在桌面空白处右键属性-->桌面--& ...
- dede:list及dede:arclist 按权重排序的方法
有时我们需要做文章排名,比如指定第一名到第三名在前面,这样就用到这个权重排序方法.稍改下就可以完美支持.. dede:list 的方法 1 找到"根目录\include\arc.listvi ...
- ASP.NET网络爬虫小研究 HtmlAgilityPack基础,爬取数据保存在数据库中再显示再自己的网页中
1.什么是网络爬虫 关于爬虫百度百科这样定义的:网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些 ...
- 邓_ phpcms_
{pc:content action="lists" catid="$catid" num="10" order="id DESC ...
- Flex: Holy Grail
Flex:Holy Grail <html> <head> <style type="text/css"> body,div,header,ma ...
- winform打开本地html页面
有时候为了提高开发效率和后期可维护性,把cs里面嵌套了远程网页,这样方便后期升级.比如,美图秀秀,qq音乐PC都嵌套了本地和远程网页.在页面拖入控件System.Windows.Forms.WebBr ...
- Traits技法
扮演"特性萃取机"角色,萃取各个迭代器的特性(迭代器的相应类型) 模板特例化:提供一份template定义式,而其本身仍为templatization 通过class templa ...
- 解决publish不编译问题
突然发现上午写的博客没了,是我没保存吗?再写一遍 eclipse下将项目部署到tomcat,run之后页面访问出现404,看日志没异常,但出现了No mapping found for HTTP re ...