- 浏览: 45887 次
- 性别:
- 来自: 叶柏寿
-
最近访客 更多访客>>
文章分类
最新评论
-
top_kevin:
fgd
JVM加载class文件的原理机制 -
0640110146:
按照这个方法解决,不过貌似每个包下面都有一个svn目录,每个 ...
svn文件被锁不能提交解决办法
Java 语言是一种具有动态性的解释型编程语言,当指定程序运行的时候, Java 虚拟机就将编译生成的 . class 文件按照需求和一定的规则加载进内存,并组织成为一个完整的 Java 应用程序。 Java 语言把每个单独的类 Class 和接口 Implements 编译成单独的一个 . class 文件,这些文件对于 Java 运行环境来说就是一个个可以动态加载的单元。正是因为 Java 的这种特性,我们可以在不重新编译其它代码的情况下,只编译需要修改的单元,并把修改文件编译后的 . class 文件放到 Java 的路径当中, 等到下次该 Java 虚拟机器重新激活时,这个逻辑上的 Java 应用程序就会因为加载了新修改的 .class 文件,自己的功能也做了更新,这就是 Java 的动态性。
下面用一个简单的例子让大家对 Java 的动态加载有一个基本的认识:
- class <SPAN style="COLOR: #000000; BACKGROUND-COLOR: #ffffff">TestClassA</SPAN>{
- public void method(){
- System.out.println("Loading ClassA");
- }
- }
- public class ClassLoaderTest {
- public static void main(String args[]){
- TestClassA testClassA = new TestClassA();
- testClassA.method();
- }
- }
class TestClassA{
public void method(){
System.out.println("Loading ClassA");
}
}
public class ClassLoaderTest {
public static void main(String args[]){
TestClassA testClassA = new TestClassA();
testClassA.method();
}
}
编译后输入命令: java -verbose:class ClassLoaderTest ,执行文件。
从运行结果我们可以看到, JRE ( JavaRuntime Environment )首先加载 ClassLoaderTest 文件,然后再加载 TestClassA 文件,从而实现了动态加载。
1. 预先加载与依需求加载
Java 运行环境为了优化系统,提高程序的执行速度,在 JRE 运行的开始会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中,因为这些单元在 Java 程序运行的过程当中经常要使用的,主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件。
当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。
我们可以看到多个基础类被加载, java.lang.Object,java.io.Serializable 等等。
相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗,因为 Java 语言的设计初衷就是面向嵌入式领域的。
在这里还有一点需要说明的是, JRE 的依需求加载究竟是在什么时候把类加载进入内部的呢?
我们在定义一个类实例的时候,比如 TestClassA testClassA ,这个时候 testClassA 的值为 null ,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行 testClassA = new TestClassA() 以后, JRE 才正真把 TestClassA 加载进来。
2. 隐式加载和显示加载
Java 的加载方式分为隐式加载( implicit )和显示加载( explicit ),上面的例子中就是用的隐式加载的方式。所谓隐式加载就是我们在程序中用 new 关键字来定义一个实例变量, JRE 在执行到 new 关键字的时候就会把对应的实例类加载进入内存。隐式加载的方法很常见,用的也很多, JRE 系统在后台自动的帮助用户加载,减少了用户的工作量,也增加了系统的安全性和程序的可读性。
相对于隐式加载的就是我们不经常用到的显示加载。所谓显示加载就是有程序员自己写程序把需要的类加载到内存当中,下面我们看一段程序:
- class TestClass{
- public void method(){
- System.out.println("TestClass-method");
- }
- }
- public class CLTest {
- public static void main(String args[]) {
- try{
- Class c = Class.forName("TestClass");
- TestClass object = (TestClass)c.newInstance();
- object.method();
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
class TestClass{ public void method(){ System.out.println("TestClass-method"); } } public class CLTest { public static void main(String args[]) { try{ Class c = Class.forName("TestClass"); TestClass object = (TestClass)c.newInstance(); object.method(); }catch(Exception e){ e.printStackTrace(); } } }
我们通过 Class 类的 forName (String s) 方法把自定义类 TestClass 加载进来,并通过 newInstance ()方法把实例初始化。事实上 Class 类还很多的功能,这里就不细讲了,有兴趣的可以参考 JDK 文档。
Class 的 forName() 方法还有另外一种形式: Class forName(String s, boolean flag, ClassLoader classloader) , s 表示需要加载类的名称, flag 表示在调用该函数加载类的时候是否初始化静态区, classloader 表示加载该类所需的加载器。
forName (String s) 是默认通过 ClassLoader.getCallerClassLoader() 调用类加载器的,但是该方法是私有方法,我们无法调用,如果我们想使用 Class forName(String s, boolean flag, ClassLoader classloader) 来加载类的话,就必须要指定类加载器,可以通过如下的方式来实现:
- Test test = new Test();//Test 类为自定义的一个测试类;
- ClassLoader cl = test. getClass().getClassLoader();
- // 获取 test 的类装载器;
- Class c = Class.forName("TestClass", true, cl);
Test test = new Test();//Test 类为自定义的一个测试类; ClassLoader cl = test. getClass().getClassLoader(); // 获取 test 的类装载器; Class c = Class.forName("TestClass", true, cl);
因为一个类要加载就必需要有加载器,这里我们是通过获取加载 Test 类的加载器 cl 当作加载 TestClass 的类加载器来实现加载的。
3. 自定义类加载机制
之前我们都是调用系统的类加载器来实现加载的,其实我们是可以自己定义类加载器的。利用 Java 提供的 java.net.URLClassLoader 类就可以实现。下面我们看一段范例:
- try{
- URL url = new URL("file:/d:/test/lib/");
- URLClassLoader urlCL = new URLClassLoader(new URL[]{url});
- Class c = urlCL.loadClass("TestClassA");
- TestClassA object = (TestClassA)c.newInstance();
- object.method();
- }catch(Exception e){
- e.printStackTrace();
- }
try{ URL url = new URL("file:/d:/test/lib/"); URLClassLoader urlCL = new URLClassLoader(new URL[]{url}); Class c = urlCL.loadClass("TestClassA"); TestClassA object = (TestClassA)c.newInstance(); object.method(); }catch(Exception e){ e.printStackTrace(); }
我们通过自定义的类加载器实现了 TestClassA 类的加载并调用 method ()方法。分析一下这个程序:首先定义 URL 指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 ) 。上述范例当中我们从 file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。
4. 类加载器的阶层体系
讨论了这么多以后,接下来我们仔细研究一下 Java 的类加载器的工作原理:
当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。
下面的图形可以表示三者之间的关系:
父类
父类
载入
载入
BootstrapLoader
PARENT
AppClassLoader
PARENT
ExtClassLoader
这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:
BootstrapLoader : sun.boot.class.path
ExtClassLoader: java.ext.dirs
AppClassLoader: java.class.path
这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。
5. 总结
了解 Java 的类加载机制对我们熟练灵活运用 Java 语言,提高程序的运行效率有着非常重要的作用,知其然也要知其所以然,这样才能从整体提高程序的质量。
- public class ClassLoaderTest1{
- private ClassLoaderTest2 test = null;
- public ClassLoaderTest1(){
- test = new ClassLoaderTest2();
- }
- public void method(){
- System.out.println("Loading ClassA");
- }
- }
- class ClassLoaderTest2{
- public ClassLoaderTest2(){
- }
- public void method(){
- System.out.println("Loading ClassA");
- }
- }
public class ClassLoaderTest1{ private ClassLoaderTest2 test = null; public ClassLoaderTest1(){ test = new ClassLoaderTest2(); } public void method(){ System.out.println("Loading ClassA"); } } class ClassLoaderTest2{ public ClassLoaderTest2(){ } public void method(){ System.out.println("Loading ClassA"); } }
测试程序:
- URL url = null;
- try {
- url = new URL("file:/E:/JAVA/MyProject/string/");
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
- URLClassLoader cl = new URLClassLoader(new URL[]{url});
- URLClassLoader cl1 = new URLClassLoader(new URL[]{url});
- try {
- Class tempClass = cl.loadClass("ClassLoaderTest1");
- Class tempClass2 = cl.loadClass("ClassLoaderTest2");
- Object test = tempClass.newInstance();
- System.out.println(tempClass.getClassLoader());
- System.out.println(tempClass2.getClassLoader());
- } catch (Exception e) {
- e.printStackTrace();
- }
URL url = null; try { url = new URL("file:/E:/JAVA/MyProject/string/"); } catch (MalformedURLException e) { e.printStackTrace(); } URLClassLoader cl = new URLClassLoader(new URL[]{url}); URLClassLoader cl1 = new URLClassLoader(new URL[]{url}); try { Class tempClass = cl.loadClass("ClassLoaderTest1"); Class tempClass2 = cl.loadClass("ClassLoaderTest2"); Object test = tempClass.newInstance(); System.out.println(tempClass.getClassLoader()); System.out.println(tempClass2.getClassLoader()); } catch (Exception e) { e.printStackTrace(); }
当ClassLoaderTest1,ClassLoaderTest2在当前目录和E:/JAVA/MyProject/string/都存在的时候输出为sun.misc.Launcher$AppClassLoader@1050169
sun.misc.Launcher$AppClassLoader@1050169
即都是被AppClassLoader加载的, 即使在E:/JAVA/MyProject/string/下面也存在.
当ClassLoaderTest1,ClassLoaderTest2只在E:/JAVA/MyProject/string/下存在的时候输出为
java.net.URLClassLoader@480457
java.net.URLClassLoader@1a7bf11
即都是被自定义的加载器加载的,并且也可以Object test = tempClass.newInstance();
下面一的是最关键的,因为ClassLoaderTest1需要用到ClassLoaderTest2,如果ClassLoaderTest2被AppClassLoader加载,而ClassLoaderTest1是被自定义的类加载器加载,就会出现如下错误:
java.lang.IllegalAccessError: tried to access class ClassLoaderTest2 from class ClassLoaderTest1
at ClassLoaderTest1. <init>(ClassLoaderTest1.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
at java.lang.Class.newInstance0(Class.java:308)
at java.lang.Class.newInstance(Class.java:261)
at ClassLoaderTest.main(ClassLoaderTest.java:43)
发表评论
-
一台服务器部属两个JBOSS方案
2010-01-09 17:43 1044解决办法: 修改 \jboss-4.0.4.GA\server ... -
对比一下
2010-01-07 19:29 729/** *分段批量处理 *看看什么是垃圾代码 */ ... -
DeploymentUtil.isSourceFolder(Unknown Source) 问题解决
2009-11-23 16:47 14301.安装后,部署工程hmsi时,出现error的一个错误对话框 ... -
servlet-api-2.4.jar - jar not loaded 问题解决
2009-11-23 16:45 2477信息: validateJarFile(G:\workspac ... -
web工程识别
2009-11-16 09:17 779修改工程目录下的.project文件 替换成下面 <?x ... -
java存储过程调用 MSSQL
2009-11-13 10:43 1114存储过程: --call MyProc(?,?,?)al ... -
有谁来买我的火柴
2009-11-01 21:37 689今天,在桂林繁華的中山路上,蹲在桃花江橋正式掛牌賣火柴了 有 ... -
关于static变量,成员变量和局部变量
2005-06-10 13:58 842我找到一个很经典的例子,看明白了就可以了解他们各自的特性了. ... -
NIO介绍
2007-04-20 14:03 719前言: 可悲的是我参考的这些技术资料是在02~04年的,那时 ... -
Java程序员认证模拟题及详细分析
2007-05-11 13:44 679Java程序员认证模拟题及详细分析 ------------ ... -
使用AXIS开发webservice(转载)
2008-03-26 15:37 596今天是感恩节,差点又要在公司加班了。好在Web Servic ... -
webservice 基于对象的传输
2008-03-26 15:38 995由于最近公司采用Web Services技术做企业应用集成( ... -
在axis中实现附件上传
2008-03-29 16:04 1211说明:最后两句非常关键!!! --------------- ... -
JavaME中文编码
2008-09-02 15:03 6981. 简介 本文介绍JavaME中文编码的 ... -
java时间定义及转换
2009-02-21 13:47 775时间定义及转换: 2008-04-10 11:12:12 & ... -
java Collection
2009-07-23 21:03 714Java面试中,最常被人问到的几个问题: 1. java.ut ...
相关推荐
### JVM加载Class文件详解 #### 一、Java与JVM中的Class文件加载机制概述 Java作为一种动态性极强的解释型编程语言,在程序运行时,Java虚拟机(JVM)负责将编译生成的`.class`文件加载到内存中进行执行。在Java...
JVM加载class文件的原理机制 JVM加载class文件的原理机制是Java虚拟机中一个非常重要的组件,负责将class文件加载到内存中,以便Java程序的执行。下面是JVM加载class文件的原理机制的详细介绍: 类加载的原理 在...
JVM加载class文件的原理机制JVM加载class文件的原理机制 JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加
总结,JVM 类加载机制是Java平台的核心特性之一,它确保了程序的稳定运行和动态扩展能力。理解类加载器的工作原理和双亲委派模型对于优化程序性能、解决类冲突以及构建复杂的模块化系统至关重要。在实际开发中,掌握...
Java虚拟机(JVM)的Class文件原理是Java程序运行的...总的来说,JVM的Class文件原理涉及到类加载器的层次结构、类的加载方式以及类加载的动态机制。理解这些原理对于优化Java程序性能和解决类加载相关问题至关重要。
Java热加载Class文件是开发者在调试和优化代码时非常实用的一种技术,它允许开发者在不重启JVM的情况下更新和替换已加载的类,极大地提高了开发效率。本文将深入探讨Java热加载Class文件的相关知识点。 首先,理解...
1. **加载**:JVM通过类加载器(ClassLoader)找到并读取.class文件。类加载器按照双亲委托模型工作,首先询问父加载器,如果父加载器无法加载,则由当前加载器尝试。加载过程中,JVM会创建一个对应的Class对象。 2...
### JVM实战-JVM类加载机制案例分析 #### 实验背景与目标 本次实验的主要目的是深入理解Java虚拟机(JVM)中的类加载机制。通过实践操作,掌握类的加载、连接与初始化过程,了解不同类型的类加载器及其工作原理,...
通过对JVM执行子系统原理的深入分析,我们不仅了解了Class文件的具体结构和字节码指令的基本概念,还探讨了类加载机制的各个环节以及字节码执行引擎的核心组成部分。这些知识点对于深入理解JVM的工作原理、优化程序...
"class文件热加载,上传class文件实现热加载"这个主题主要涉及到Java应用的运行时动态更新机制。下面将详细介绍这个过程及其相关知识点。 1. **Java类加载器**: - Java虚拟机(JVM)通过类加载器来加载类。默认有...
类加载机制JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述: 1. Bootstrap ClassLoader /启动类加载器 $JAVA_HOME 中 jre/lib/rt.jar 里所有的 class,由 C++实现,不是 ...
描述一下 JVM 加载 Class 文件的原理机制? 在面试java工程师的时候,这道题经常被问到,故需特别注意。 Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件...
总结起来,"JVM之用Java解析class文件"这一主题涵盖了从class文件的结构到JVM加载机制的多个层面。通过学习这一领域,开发者可以更好地理解Java程序的生命周期,提高问题诊断能力,并能利用字节码技术实现高级编程...
- **加载Class文件**:解析并加载Java程序编译后的字节码文件(.class文件)。 - **调用本地接口**:通过JNI (Java Native Interface) 调用本地代码库中的函数。 - **解释命令**:将字节码指令转换为对应平台的机器码...
1. JVM加载Class文件的原理机制 JVM加载Class文件的过程分为三个主要阶段:装载、连接和初始化。 - **装载**:这是加载过程的第一步,JVM会找到并读取类文件。Java类加载器负责这个任务,它会将磁盘上的类文件加载...
Java的类加载机制是JVM确保Java应用程序运行的关键部分,它由多个阶段构成,包括加载、验证、准备、解析和初始化。了解这些机制可以帮助开发者有效地管理Java类的生命周期,实现类的动态加载与卸载,并对程序性能...
### JVM 类加载机制与双亲委派模型 #### 一、概述 JVM(Java Virtual Machine,Java虚拟机)中的类加载机制是一项重要的基础技术,它不仅涉及到了类的加载过程,还涉及到类如何被正确识别及加载的问题。类加载器...