Java Class Loader
Reference:
[1] http://www.cnblogs.com/kevin2chen/p/6714214.html
当调用 java命令运行一个java程序时,会启动一个java虚拟机进程。同一个jvm的所有线程、所有变量都处于同一个进程里,都使用该jvm进程的内存区。 jvm进程终止,jvm内存中的数据将全部丢失。
jvm进程终止的情况:
1.程序运行到最后正常结束。
2.遇到System.exit()或Runtime.getRuntime.exit()。
3.遇到未捕获的异常或错误
4.程序所在的平台强制结束了JVM进程

类的加载
当程序主动使用某个类时,如果该类还未被加载到内存中,系统会进行类加载。类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class的实例。因为java中万物皆为对象,类也是java.lang.Class类型的对象。
类加载具体有加载、连接和初始化3个步骤,加载阶段需要完成的事情有:
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在java堆中生成一个代表这个类的Class对象,作为访问方法区中这些数据的入口。
类的加载,是由类加载器来完成的,类加载器通常由JVM提供。通过使用不同的类加载器,可以加载以下不同来源的类的二进制数据:
1.从本地文件系统加载class文件
2.从jar包中直接加载class文件
3.通过网络加载class文件
4.把一个java源文件动态编译并加载
类的连接
负责把类的二进制数据合并到JRE(java运行环境)中
1.验证:检测被加载的类是否有正确的内部结构,是否被破坏或包含不良代码,并和其他类协调一致
2.准备:负责为类的静态属性分配内存,并设置默认初始值
3.解析:将类的二进制数据中的符号引用替换成直接引用
类初始化
主要对类的静态属性进行初始化。可以在声明静态属性时指定初始值和通过静态初始化块指定初始值,JVM会按照这些语句在程序中的顺序依次执行。
类加载器
每个类加载器(除了根类加载器)都是java.lang.ClassLoader的实例,负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。
同一个.class不会被同一个类加载器加载两次,如何判断是同一个类: java中,一个类用其全限定类名标识--包名+类名;jvm中,一个类用其全限定类名+其类加载器标识---包名+类名+类加载器名
加载器层次结构:
JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:
1.Bootstrap ClassLoader 根类加载器(引导类加载器),加载java的核心类(限定的加载路径:jdk1.8_02/jre/lib/rt.jar)。它不是java.lang.ClassLoader的子类,而是由JVM自身实现的。虚拟机为了安全性以及功能的完整性,并不是任何存在于启动类加载器路径下的jar都会被加载,只有可信类(trusted classes )会被加载,所以开发者不要将自定义的类放在此目录。
2.Extension ClassLoader 扩展类加载器,加载JRE的扩展目录中JAR的类包(限定的加载路径:%JAVA_HOME%/jre/lib/ext/或java.ext.dirs系统属性指定的目录)
3.System ClassLoader 系统类加载器,加载java.class.path系统属性或CLASSPATH环境变量所指定的jar包和类路径,可通过ClassLoader的静态方法ClassLoader.getSystemClassLoader()获取系统类加载器。如果没有特别指定,用户自定义的类加载器以该类加载器作为它的父加载器。也可以通过Class实例的getClassLoader()方法当前类的类加载器。
4.自定义类加载器
JVM系统自带的类加载器在程序运行中只能加载对应路径的.class文件,无法改变其搜索路径。如果想在运行时从其他路径加载类,就要编写自定义的类加载器。
类加载的机制
1.全盘负责:某类所依赖及其引用的所有类,都由同一个加载器负责加载,除非显示使用另外一个加载器。
2.双亲委托: 当一个类加载器收到类加载的请求,首先将请求委派给父类加载器,递归到Bootstrap ClassLoader。然后加载器根据请求尝试搜索和加载类,若无法加载该类时则向子类加载器反馈信息(抛出ClassNotFoundException),由子类加载器自己去加载。
类加载期之间的父子关系并不是类继承上的父子关系,是采用组合的方式实现双亲委派模型的。
根类加载器是由c++实现的,不是由java语言实现,没有继承ClassLoader,所以扩展类加载器调用parent()返回的是null。但扩展类加载器实际上仍可以委派给根类加载器。
使用双亲委托模式的原因:被父类加载器加了的类可以避免避免被子类重新加载,因为在JVM中由全限定名+类加载器名标识类。另外可以避免加载到同sun公司核心API同名的恶意类。
自定义类加载器
JVM中除了根加载器之外,所有类加载器都是ClassLoader子类的实例,开发者通过扩展ClassLoader并重写ClassLoader所包含的方法来实现自定义的类加载器。
如上源码所示,loadClass()方法内部主要是双亲委派模型的实现,在最后调用findClass()方法加载类,所以自定义类加载器重写findClass()比重写loadClass()方便,可以避免覆盖默认类加载器的父类委托和缓冲机制两种策略。
ClassLoader类的核心方法:
protected synchronized Class<?> loadClass(String name, boolean resolve)根据指定的binary name(由两个不同部分组成的名字,即全限定类名)来加载类,返回Class对象。
protected Class<?> findClass(String name)根据二进制名称来查找类,返回Class对象。此方法应该被自定义类加载器的实现重写,在通过父类加载器检查所请求的类后,被loadClass方法调用。
final defineClass(String name,byte[] b,int off,int len)将指定类的字节码文件读入字节数组内,并把它转为Class实例,该字节码文件可以来源于网络或本地。
findLoadedClass(String name)返回jvm装载的name类的Class实例,若无返回null。
static getSystemClassLoader()返回系统类加载器。
findSystemClass(String name)从本地文件系统加载class文件,并生成Class对象。
使用自定义类加载器可以实现如下功能:
执行代码前自动验证数字签名;
根据用户提供的密码解密代码,从而可以实现代码混淆器来避免反编译class文件;
根据用户需求来动态的加载类;
根据应用需求把其他数据以字节码的形式加载到应用中;
URLClassLoader 类
java 为ClassLoader提供了一个实现类URLClassLoader ,该类也是系统类加载器和扩展类加载器的父类,它可以从本地文件系统和远程主机获取二进制文件来加载类。
URLClassLoader的两个构造器
URLClassLoader(URL[] urls):使用默认的父类加载器创建一个ClassLoader对象,该对象将从urls所指定的系列路径来查询并加载类。
URLClassLoader(URL[] urls, ClassLoader parent):使用指定的父类加载器创建。。。
一旦获得URLClassLoader对象后,就可以调用类加载器的 loadClass()方法来加载指定的类。
加载图片、视频、文本等非类资源
ClassLoader除了用于加载类外,还可以用于加载图片、视频等非类资源,也是采用双亲委派模型将加载资源的请求传递到顶层的Bootstrap ClassLoader,在其对应的目录下搜索资源,若失败才逐层返回并搜索。
相关的实例方法:
URL getResource(String name):资源名称name是以 '/' 分隔的标识资源的路径名称,返回资源的 URL 对象的枚举。
InputStream getResourceAsStream(String name):返回读取指定资源的输入流。
Enumeration<URL> getResources(String name)
1)class.getResource(String path):返回URL对象
path以'/'开头时,'/'表示ClassPath,从项目的classpath下查询资源;
path不以'/'开头时,是从此类所在的包下查询资源;
步骤:先递归在所有parent classLoader的classpath里查找resource,如果未找到,则在JVM内置的calss loader的路径中查找。
2)class.getClassLoader().getResource(String name):返回URL对象
The name of a resource is a '/'-separated path name that identifies the resource.
name不能以'/'开头,指资源标识符(即相对于classpath的路径)。一般web项目的classpath路径为……/webapps/appName/WEB-INF/classes/。如果资源不在此目录下,需要用使用相对路径做调整。
String path= MyService.class.getClassLoader().getResource("config.properties").getPath();
Properties prop = new Properties();
prop.load(new FileReader(path));
Java Class Loader的更多相关文章
- Java Class Loader Retrospect
- Java: some learning note for Java calssloader and Servlet
1. Java Classloader 链接: https://en.wikipedia.org/wiki/Java_Classloader 摘要: The Java Classloader is a ...
- java 运行指定类的main函数
运行jar文件的方法是: java -jar xxx.jar 但是有时,我们希望运行里面的具体某个类,这时可以通过: java -cp xxx.jar xxx.com.xxxx 它会找到这个类的ma ...
- Tomcat从零开始(十)Loader
第十课: 不知不觉就10篇blog了,说实话,我是第一次更这么长时间的Blog. 嗯,今天说说Loader,在以前的课程中,也就是内个能使用最初级的servlet的那一节,我们使用了URLClassL ...
- Java 9 揭秘(8. JDK 9重大改变)
Tips 做一个终身学习的人. 在本章,主要介绍以下内容: 新的JDK版本控制方案是什么 如何使用Runtime.Version类解析JDK版本字符串 JDK JRE 9的新目录布局是什么 JDK 9 ...
- 详解Java内存区域?虚拟机类加载机制?
一.Java运行时数据区域 1.程序计数器 “线程私有”的内存,是一个较小的内存空间,它可以看做当前线程所执行的字节码的行号指示器.Java虚拟机规范中唯一一个没有OutOfMemoryError情况 ...
- 探秘 Java 热部署三(Java agent agentmain)
前言 让我们继续探秘 Java 热部署.在前文 探秘 Java 热部署二(Java agent premain)中,我们介绍了 Java agent premain.通过在main方法之前通过类似 A ...
- 探秘 Java 热部署
# 前言 在之前的 深入浅出 JVM ClassLoader 一文中,我们说可以通过修改默认的类加载器实现热部署,但在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能 ...
- 探秘 Java 热部署二(Java agent premain)
# 前言 在前文 探秘 Java 热部署 中,我们通过在死循环中重复加载 ClassLoader 和 Class 文件实现了热部署的功能,但我们也指出了缺点-----不够灵活.需要手动修改文件等操作. ...
随机推荐
- 老李分享:HTTP协议之请求和响应
老李分享:HTTP协议之请求和响应 HTTP请求头详解: GET http://www.foo.com/ HTTP/1.1 GET是请求方式,请求方式有GET/POST http://www.fo ...
- 关于C++中的前置声明(附程序运行图)
实验于华中农业大学逸夫楼2017.3.10 在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration).下面的程序中,带注释的那行就是类B的前置说明.这是必须的,因为类A中 ...
- ios 获取当前设备信息、内存
//在[UIDevice currentDevice]中的属性 @property(nonatomic,readonly,strong) NSString *name; // e.g. "M ...
- 树状数组 && 线段树
树状数组 支持单点修改 #include <cstdio> using namespace std; int n, m; ], c[]; int lowbit(int x) { retur ...
- Javascript数组与基本函数
数组定义方法: 1. var arr=new Array(); var arr=Array(); 2. var arr1=Array('a','b','c'); 3. var arr2=['a','b ...
- 启动LUXContentTests过程中遇到的问题
首先,要想在localbox中使用Selenium,就得准备好浏览器的driver文件.比如chrome对应的chromedriver文件,该文件是一个exe可执行文件. 问题:当我尝试去跑LUXCo ...
- SELECT中的多表连接
MySQL多表连接查询 连接(join):将一张表中的行按照某个条件(连接条件)和另一张表中的行连接起来形成一个新行的过程. 根据连接查询返回的结果,分3类: 内连接(inner join) 外连接( ...
- 容易产生错误的where条件
错误的方式:$where = [];if ($type == 'wait') { $where['status'] = 0;}if ($type == 'done') { $where['status ...
- tomcat 下部署单框架cas时,报出org.apache.jasper.JasperException异常的解决办法
在tomcat中部署好cas server(设置好https,将cas.war拷贝到了webapps下部署完成),启动tomcat后,访问http://localhost:8443/cas/login ...
- oracle中varchar、varchar2、char和nvarchar的区别
1.char char的长度是固定的,比如说,你定义了char(20),即使你你插入abc,不足二十个字节,数据库也会在abc后面自动加上17个空格,以补足二十个字节: char是区分中英文的,中文在 ...