文档章节

深入研究Java类加载机制

浮躁的码农
 浮躁的码农
发布于 2016/04/19 09:50
字数 1763
阅读 40
收藏 2

 
类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行。
研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。
 
一、简单过程
 
Java程序运行的场所是内存,当在命令行下执行:
java HelloWorld
命令的时候,JVM会将HelloWorld.class加载到内存中,并形成一个Class的对象HelloWorld.class。
其中的过程就是类加载过程:
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。
4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。
5、最后由AppClass Loader加载HelloWorld类。
 
以上就是类加载的最一般的过程。
 
二、类加载器各自搜索的目录
 
为了弄清楚这个问题,首先还要看看System类的API doc文档。
 
 
相关值的描述
java.version Java 运行时环境版本
java.vendor Java 运行时环境供应商
java.vendor.url Java 供应商的 URL
java.home Java 安装目录
java.vm.specification.version Java 虚拟机规范版本
java.vm.specification.vendor Java 虚拟机规范供应商
java.vm.specification.name Java 虚拟机规范名称
java.vm.version Java 虚拟机实现版本
java.vm.vendor Java 虚拟机实现供应商
java.vm.name Java 虚拟机实现名称
java.specification.version Java 运行时环境规范版本
java.specification.vendor Java 运行时环境规范供应商
java.specification.name Java 运行时环境规范名称
java.class.version Java 类格式版本号
java.class.path Java 类路径
java.library.path 加载库时搜索的路径列表
java.io.tmpdir 默认的临时文件路径
java.compiler 要使用的 JIT 编译器的名称
java.ext.dirs 一个或多个扩展目录的路径
os.name 操作系统的名称
os.arch 操作系统的架构
os.version 操作系统的版本
file.separator 文件分隔符(在 UNIX 系统中是“/”)
path.separator 路径分隔符(在 UNIX 系统中是“:”)
line.separator 行分隔符(在 UNIX 系统中是“/n”)
user.name 用户的账户名称
user.home 用户的主目录
user.dir 用户的当前工作目录
 
可惜这个帮助文档并不全,直接用程序打印出来如下:
                 for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) { 
                        System.out.println(entry.getKey()+ "\t"+entry.getValue()); 
                }
 
java.runtime.name Java(TM) SE Runtime Environment
sun.boot.library.path Q:\jdk6\jre\bin
java.vm.version 14.0-b16
java.vm.vendor Sun Microsystems Inc.
java.vendor.url http://java.sun.com/
path.separator ;
idea.launcher.port 7532
java.vm.name Java HotSpot(TM) Client VM
file.encoding.pkg sun.io
sun.java.launcher SUN_STANDARD
user.country CN
sun.os.patch.level Service Pack 3
java.vm.specification.name Java Virtual Machine Specification
user.dir E:\projects\testScanner
java.runtime.version 1.6.0_14-b08
java.awt.graphicsenv sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs Q:\jdk6\jre\lib\endorsed
os.arch x86
java.io.tmpdir C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\
line.separator  
java.vm.specification.vendor Sun Microsystems Inc.
user.variant  
os.name Windows XP
sun.jnu.encoding GBK
java.library.path Q:\jdk6\bin;.;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;Q:\jdk6\bin;Q:\JavaFX\javafx-sdk1.2\bin;Q:\JavaFX\javafx-sdk1.2\emulator\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\MySQL Server 5.1\bin;C:\Program Files\StormII\Codec;C:\Program Files\StormII
java.specification.name Java Platform API Specification
java.class.version 50
sun.management.compiler HotSpot Client Compiler
os.version 5.1
user.home d:\我的文档
user.timezone  
java.awt.printerjob sun.awt.windows.WPrinterJob
idea.launcher.bin.path C:\IDEA8\bin
file.encoding UTF-8
java.specification.version 1.6
java.class.path Q:\jdk6\jre\lib\alt-rt.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\lib\deploy.jar;Q:\jdk6\jre\lib\javaws.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\management-agent.jar;Q:\jdk6\jre\lib\plugin.jar;Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\ext\dnsns.jar;Q:\jdk6\jre\lib\ext\localedata.jar;Q:\jdk6\jre\lib\ext\sunjce_provider.jar;Q:\jdk6\jre\lib\ext\sunmscapi.jar;Q:\jdk6\jre\lib\ext\sunpkcs11.jar;E:\projects\testScanner\out\production\testScanner;C:\IDEA8\lib\idea_rt.jar
user.name Administrator
java.vm.specification.version 1
java.home Q:\jdk6\jre
sun.arch.data.model 32
user.language zh
java.specification.vendor Sun Microsystems Inc.
awt.toolkit sun.awt.windows.WToolkit
java.vm.info mixed mode, sharing
java.version 1.6.0_14
java.ext.dirs Q:\jdk6\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
sun.boot.class.path Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\sunrsasign.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\classes
java.vendor Sun Microsystems Inc.
file.separator \
java.vendor.url.bug http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding UnicodeLittle
sun.cpu.endian little
sun.desktop windows
sun.cpu.isalist  
 
1、Bootstrap Loader(启动类加载器):加载System.getProperty("sun.boot.class.path")所指定的路径或jar。
2、Extended Loader(标准扩展类加载器ExtClassLoader):加载System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld
 
3、AppClass Loader(系统类加载器AppClassLoader):加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld
 
ExtClassLoader和AppClassLoader在JVM启动后,会在JVM中保存一份,并且在程序运行中无法改变其搜索路径。如果想在运行时从其他搜索路径加载类,就要产生新的类加载器。
 
三、类加载器的特点
 
1、运行一个程序时,总是由AppClass Loader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3、Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
 
四、类加载器的获取
 
很容易,看下面例子
public  class HelloWorld { 
         public  static  void main(String[] args) { 
                HelloWorld hello =  new HelloWorld(); 
                Class c = hello.getClass(); 
                ClassLoader loader = c.getClassLoader(); 
                System.out.println(loader); 
                System.out.println(loader.getParent()); 
                System.out.println(loader.getParent().getParent()); 
        } 
}
 
打印结果:
sun.misc.Launcher$AppClassLoader@19821f 
sun.misc.Launcher$ExtClassLoader@addbf1 
null 

Process finished with exit code 0
 
从上面的结果可以看出,并没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(启动类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。
 
五、类的加载
 
类加载有三种方式:
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
 
三种方式区别比较大,看个例子就明白了:
public  class HelloWorld { 
         public  static  void main(String[] args)  throws ClassNotFoundException { 
                ClassLoader loader = HelloWorld. class.getClassLoader(); 
                System.out.println(loader); 
                 //使用ClassLoader.loadClass()来加载类,不会执行初始化块 
                loader.loadClass( "Test2"); 
                 //使用Class.forName()来加载类,默认会执行初始化块 
//                Class.forName("Test2"); 
                 //使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块 
//                Class.forName("Test2", false, loader); 
        } 
}
 
public  class Test2 { 
         static { 
                System.out.println( "静态初始化块执行了!"); 
        } 
}
 
分别切换加载方式,会有不同的输出结果。
 
六、自定义ClassLoader
 
为了说明问题,先看例子:
package test; 

import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 

/** 
* 自定义ClassLoader 

* @author leizhimin 2009-7-29 22:05:48 
*/
 
public  class MyClassLoader { 
         public  static  void main(String[] args)  throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { 
                URL url =  new URL( "file:/E:\\projects\\testScanner\\out\\production\\testScanner"); 
                ClassLoader myloader =  new URLClassLoader( new URL[]{url}); 
                Class c = myloader.loadClass( "test.Test3"); 
                System.out.println( "----------"); 
                Test3 t3 = (Test3) c.newInstance(); 
        } 
}
 
public  class Test3 { 
         static { 
                System.out.println( "Test3的静态初始化块执行了!"); 
        } 
}
 
运行后:
---------- 
Test3的静态初始化块执行了! 

Process finished with exit code 0
 
可以看出自定义了ClassLoader myloader = new URLClassLoader(new URL[]{url});已经成功将类Test3加载到内存了,并通过默认构造方法构造了对象Test3 t3 = (Test3) c.newInstance();
 
有关ClassLoader还有很重要一点:
同一个ClassLoader加载的类文件,只有一个Class实例。但是,如果同一个类文件被不同的ClassLoader载入,则会有两份不同的ClassLoader实例(前提是着两个类加载器不能用相同的父类加载器)。

本文转载自:http://lavasoft.blog.51cto.com/62575/184547/

共有 人打赏支持
浮躁的码农

浮躁的码农

粉丝 62
博文 727
码字总数 145372
作品 0
松江
程序员
一线互联网公司研发工程师(Java)月薪25-50k的技能要求整理

良好的开发基础,精通Java语言基础,熟练掌握多线程开发技术, 对于Java基础技术体系(包括JVM、类装载机制、JUC、NIO、网络)有深入的理解和实践经验; 熟练掌握常用Java技术框架,能编写高...

vvx1024
01/06
0
0
深入研究java.lang.ThreadLocal类

一、概述 ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局...

FoxHu
2012/05/08
0
0
Java Classloader机制解析

做Java开发,对于ClassLoader的机制是必须要熟悉的基础知识,本文针对Java ClassLoader的机制做一个简要的总结。因为不同的JVM的实现不同,本文所描述的内容均只限于Hotspot Jvm. 本文将会从...

aminqiao
2014/05/09
0
2
JDK源码之ClassLoader

Java类加载器ClassLoader总结 JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。 2.显式装载, 通过class.for...

村长大神
2014/03/27
0
0
ClassLoader加载Class的过程 解析

ClassLoader即类加载器,负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象,当 JVM 启动时,会形成由三个类加载器组成的初始类加载器...

phacks
2015/08/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Redis大key搜索

一、背景信息 Redis 提供了 list、hash、zset 等复杂类型的数据结构,业务在使用的时候可能由于 key 设计不合理导致某个 key 过大。由于 redis 简单的单线程模型,业务在获取或者删除大 key ...

IT--小哥
16分钟前
0
0
Tcl命令操作实验-----(5)

Vivado% proc myproc {arg} {puts $arg}Vivado% myproc mynamemynameVivado% if {2>1} {puts 2>1} else {puts 2<1}2>1...

whoisliang
32分钟前
0
0
比特币钱包RPC的PHP调用方法

当我们希望在Php开发的网站中加入比特币支付功能时,需要解决的第一个 问题,就是如何在Php程序代码中调用比特币钱包的RPC API开发接口来实现 我们期望的功能,例如比特币的支付与接收。 例如...

汇智网教程
33分钟前
0
0
crunch 練習

crunch 10 10 -t 09%%%%%%%% -o tw_mobile_number.lst

BaiyuanLab
49分钟前
0
0
Kafka技术资料总结(不断更新中)

1、Kafka实践:到底该不该把不同类型的消息放在同一个主题中 2、Kafka剖析系列: Kafka剖析(一):Kafka背景及架构介绍 Kafka设计解析(二):Kafka High Availability (上) Kafka设计解析...

九州暮云
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部