从事java研发必然少不了对java类加载机制的涉及,本文结合例子讲述java classloader工作机制。
一 jvm 类加载机制
1)jvm位置:java是运行在java虚拟机上的程式,java虚拟机物理层面上来讲,就是我们安装在电脑上的jre目录/lib/jvm.dll(版本不同,可能存在于jre目录/lib/client/jvm.dll,jre目录/lib/server/jvm.dll),这是java字节码运行的基础,它不是由java语言编写,所以我们阅读jdk源码时遇到native函数,基本上就是调用jvm相关的代码。
2)jdk和jre关系:从oracle官网上下载java环境,可以选择jdk或者jre进行安装,他们的关系可以理解为子集的概念,jdk是jre运行环境再加上一些java开发的工具集,查看jdk目录结构如下(例子为jdk1.6.37版本)
D:. ├─bin │ └─server ├─include │ └─win32 ├─jre │ ├─bin │ │ ├─dtplugin │ │ ├─plugin2 │ │ └─server │ └─lib │ ├─amd64 │ ├─applet │ ├─audio │ ├─cmm │ ├─deploy │ ├─ext │ ├─fonts │ ├─im │ ├─images │ │ └─cursors │ ├─management │ ├─security │ ├─servicetag │ └─zi │ ├─Africa │ ├─America │ │ ├─Argentina │ │ ├─Indiana │ │ ├─Kentucky │ │ └─North_Dakota │ ├─Antarctica │ ├─Asia │ ├─Atlantic │ ├─Australia │ ├─Etc │ ├─Europe │ ├─Indian │ ├─Pacific │ └─SystemV └─lib └─visualvm ├─etc ├─platform │ ├─config │ │ ├─ModuleAutoDeps │ │ └─Modules │ ├─core │ │ └─locale │ ├─docs │ ├─lib │ │ └─locale │ ├─modules │ │ ├─ext │ │ │ └─locale │ │ └─locale │ └─update_tracking ├─profiler │ ├─config │ │ └─Modules │ ├─lib │ │ ├─deployed │ │ │ ├─jdk15 │ │ │ │ └─windows-amd64 │ │ │ └─jdk16 │ │ │ └─windows-amd64 │ │ └─locale │ ├─modules │ │ └─locale │ └─update_tracking └─visualvm ├─config │ └─Modules ├─core │ └─locale ├─modules │ └─locale └─update_tracking
java官方文档描述jre和jdk关系如图:(链接http://docs.oracle.com/javase/7/docs/)
在安装jdk时可以选择是否同时安装jre,如果选择安装,那么系统中就存在两份jre,具体程序运行时会执行哪个jre,windows系统默认搜索规则是:
1. 当前目录下有沒有 JRE子目录
2. 父目录下 JRE 子目录
3.查 詢 Window Registry(HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\)
注意:安装环境会建议建立JAVA_HOME环境变量并将其加入path中,这样可以避免因为默认搜索规则出的结果造成混淆。如果没有加,可以搜索下自己系统中有几个java.exe,本人系统中有三个,分别在c:/windows/system32/java.ext;
d:/program files/java/jdk_1_6_37/bin/java.exe;
d:/program files/java/jre/bin/java.exe
因为没有将JAVA_HOME路径加入到path,path路径是c:/windows/system32;......所以在命令行下执行java Main系统默认执行的是c:/windows/system32/java.exe(除非命令行在其他两个java.exe所在目录),这一点可以通过分别修改三个路径下java.exe文件到新名字java1.exe来验证到底执行的是哪个目录
3)java类加载机制:jdk带有三个系统类加载器:bootstrap加载器;扩展加载器;系统加载器,他们的关系如下表
类加载器 |
被加载加载器 | parent | 父类 | 类型 | 默认加载目录/文件 | 备注 |
bootstrap加载器 |
sun.boot.class.path系统属性所指路径,指向jre下/lib,如rt.jar
|
虚拟机出于安全等因素考虑,不会加载< Java_Runtime_Home >/lib存在的陌生类,开发者通过将要加载的非JDK自身的类放置到此目录下期待启动类加载器加载是不可能的 |
||||
扩展加载器 |
bootstrap加载器 |
bootstrap加载器(因为此加载器由非java语言编写,在jvm中标识为null,所以一个加载器的parent为null表示它是由bootstrap加载器加载) |
java.lang.ClassLoader->java.security.SecurClassLoader->java.net.URLClassLoader | sun.misc.Launcher$ExtClassLoader | java.ext.dirs属性所指路径,指向java.exe所在jre下/lib/ext子目录,可以将自己的class文件放入这个目录,交由扩展加载器加载,可以通过–Djava.ext.dirs=xxx 改变 | jvm中只存在一份,一旦建立,再通过System.setProperty()修改系统属性不会起作用 |
系统加载器 |
bootstrap加载器 |
扩展加载器 |
java.lang.ClassLoader->java.security.SecurClassLoader->java.net.URLClassLoader | sun.misc.Launcher$AppClassLoader |
默认为.目录
再取java.class.path属性所指路径,可以通过java -cp xxx 来改变 最后取环境变量CLASSPATH下的class文件和jar文件 |
jvm中只存在一份,一旦建立,再通过System.setProperty()修改系统属性不会起作用 |
在 Java 之中,每个类都是由某个类型加载器(ClassLoader 的实体)来载入,因此,Class 类型的实体中,都会有记录载入它的ClassLoader 的实体(注意:如果值是null,不代表它不是由类加载器载入,而是代表这个类別是由(bootstrap loader,也有人称root loader)所载入,只不过这个类型加载器不由java书写,所以逻辑上没有实体 )
二 自定义类加载器
加载类到内存中分两种方式:1)预加载 ;2)显示加载。预加载是虚拟机在启动的时候将rt.jar中的类一次加载到内存,因为这些类都是基础类,会被频繁使用到,预加载可以减少运行时IO开销,显示加载可以:1)使用new()操作符 2)java.lang.Class 裡的forName() 3)java.lang.ClassLoader 裡的loadClass()
要查看类加载详情,可以使用java -verbose:class xxx来输出。看下面一段代码:
public class Main { public static void main(String args[]) { A a1 = new A() ; a1.print() ; B b1 = new B() ; b1.print() ; } } public class A //与Main在同一个路径下 { public void print() { System.out.println("Using Class A") ; } } public class B //与Main在同一个路径下 { public void print() { System.out.println("Using Class B") ; } }到Main所在目录执行javac *.java,查看生成了三个calss文件,再执行java -verbose:class Main > load.log ,查看load.log内容如下:
[Opened D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] [Loaded java.lang.Object from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] [Loaded java.io.Serializable from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] [Loaded java.lang.Comparable from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] ... ... [Loaded java.security.Principal from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] [Loaded Main from file:/D:/deep_java/] [Loaded A from file:/D:/deep_java/] Using Class A [Loaded B from file:/D:/deep_java/] Using Class B [Loaded java.lang.Shutdown from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar] [Loaded java.lang.Shutdown$Lock from D:\Program Files\Java\jdk1.6.0_37\jre\lib\rt.jar]由此可见class类加载顺序。
也可以使用以下方式加载类:
import java.net.* ; public class Test { public static void main(String args[]) throws Exception { Class c = Class.forName(args[0]) ; //第一种载入class对象方法 Object o = c.newInstance() ; //Class c = Class.forName(args[0],true,off.getClass().getClassLoader()) ;//true参数表示载入同时进行初始化,这个参数在SPI接口和实现类加载中非常有用 Test off = new Test() ; System.out.println("类型准备载入") ; //ClassLoader loader = off.getClass().getClassLoader() ;//第二种载入class对象方法,使用了对象引用Class的classloader //Class c = loader.loadClass(args[0]) ; System.out.println("类型准备实例化") ; Object o = c.newInstance() ; Object o2 = c.newInstance() ; } }
了解了默认类加载机制后,可以手工打造一个加载器,ExtClassLoader和AppClassLoader都是继承URLClassLoader,自己的加载器也可以继承自这个类:
import java.net.* ; public class Test { public static void main(String args[]) throws Exception { URL u = new URL("file:/D:/deep_java/test/lib/") ; URLClassLoader ucl = new URLClassLoader(new URL[]{ u }) ; Class c = ucl.loadClass(args[0]) ; Assembly asm = (Assembly) c.newInstance() ; asm.start() ; URL u1 = new URL("file:/D:/deep_java/test/lib/") ; URLClassLoader ucl1 = new URLClassLoader(new URL[]{ u1 }) ; Class c1 = ucl1.loadClass(args[0]) ; Assembly asm1 = (Assembly) c1.newInstance() ; asm1.start() ; System.out.println(Test.class.getClassLoader()) ; System.out.println(u.getClass().getClassLoader()) ; System.out.println(ucl.getClass().getClassLoader()) ; System.out.println(c.getClassLoader()) ; System.out.println(asm.getClass().getClassLoader()) ; System.out.println(u1.getClass().getClassLoader()) ; System.out.println(ucl1.getClass().getClassLoader()) ; System.out.println(c1.getClassLoader()) ; System.out.println(asm1.getClass().getClassLoader()) ; System.out.println(Assembly.class.getClassLoader()) ; }deep_java/test/ 目录结构如下:
├─Test.class ├─Assembly.class ├─lib │ ├─ClassA.class │ ├─ClassB.class │ ├─ClassC.classAssembly 是一个接口,ClassA ClassB ClassC都实现了这个接口,Test主程序在运行时将参数名作为Class名动态加载。命令行输入java -verbose:class Test ClassA 执行结果如下:
相关推荐
#### 一、Java类加载机制概述 Java类加载机制是Java程序运行的第一步,它对于理解Java虚拟机(JVM)的行为至关重要。类加载过程涉及到类的加载、链接(验证、准备、解析)、初始化等阶段,并且这一过程是由类加载器...
该文件是JVM中关于类加载机制的知识整理的思维导图,包括类加载机制概述、类加载的生命周期、加载时机、加载过程、类加载、类的初始化和实例化等几个大方面进行了讲解,其中类加载中还对JVM三种预定义类加载器进行了...
在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了Java程序在多线程环境中的安全性和隔离性。类加载器...
在Java世界中,类加载机制是一项极为重要的技术。它不仅关乎程序的启动与运行,更是Java动态性与灵活性的基础。本文旨在深入探讨Java类加载的过程、原理及其在实际开发中的应用。 #### 二、类与数据的概念 在Java...
#### 六、类加载机制概述 类加载机制是指Java虚拟机如何将Java类从文件系统加载到内存中,并准备好让Java程序使用的整个过程。这个过程包括了类的加载、连接和初始化三个阶段。 1. **加载**:找到并加载类的二进制...
Java 的类加载机制是其核心特性之一,虽然不是经常被提及的热门话题,但对于每一位 Java 开发者来说,掌握这一机制的工作原理至关重要。本文旨在深入剖析 Java 类加载的过程,帮助读者理解类加载的基本概念及其在 ...
1. 类加载机制概述 Java的类加载机制遵循“双亲委派模型”(Delegation Model)。当一个类被加载时,它首先会尝试由当前线程的Context ClassLoader进行加载,如果该类加载器无法加载,则向上委托给父类加载器,直至...
### Java类动态加载机制在铁路互联网售票中的设计与实现 #### 概述 随着铁路互联网售票系统的广泛应用,其面临着越来越高的并发访问压力。为解决这一问题,系统采用分布式内存数据库集群来存储和处理如余票查询、...
本次实验的主要目的是深入理解Java虚拟机(JVM)中的类加载机制。通过实践操作,掌握类的加载、连接与初始化过程,了解不同类型的类加载器及其工作原理,学会创建自定义的类加载器,并对类的卸载有所认识。实验将结合...
#### 二、类的加载机制概述 类的加载过程主要包括以下几个阶段: 1. **加载(Loading)** 2. **验证(Verification)** 3. **准备(Preparation)** 4. **解析(Resolution)** 5. **初始化(Initialization)** 6...
本文将详细介绍如何利用Java的类加载机制和类路径管理来构建一个简易的在线购物网站。该示例通过实现一个`ShoppingCart`类来管理用户的购物车信息,并提供添加商品、移除商品以及计算总价等功能。此外,我们还将探讨...
### Java反射机制与动态加载实例类 在Java中,反射是一种强大的工具,允许程序在运行时检查和修改其结构和行为。通过反射,我们可以动态地加载类、创建对象、访问和修改字段、调用方法等。本文将深入探讨Java反射...
1. Java3D概述 Java3D是一种基于Java的三维图形编程接口,由Sun Microsystems开发。它提供了一种平台独立的三维图形编程接口,允许开发者创建交互式的三维应用程序。 2. 加载3D模型文件 Java3D提供了多种方式来...
Java 类加载与反射是Java开发中的...总的来说,Java的类加载和反射机制是其动态性和灵活性的关键,理解和掌握这些概念对提升Java编程能力至关重要。在开发过程中,合理利用这些特性可以提高代码的可扩展性和维护性。
- **安全模型**:通过沙箱安全机制、类加载机制等方式保护运行中的程序不受恶意代码攻击。 #### 二、Java的关键概念与特性 Java具备一系列重要的特性和概念,使得它成为一种非常强大且易于使用的编程语言。 #####...
1. **面向对象**:Java是一个完全面向对象的编程语言,支持类、接口和继承等面向对象特性,使得代码结构清晰,易于维护和扩展。 2. **简单性**:Java的设计目标之一是简化C++的复杂性,去除了一些容易导致错误的...
1. **类加载器(Class Loader)**:Java程序中的类并非一开始就全部加载到内存,而是根据需要由类加载器动态加载。这为反射提供了基础。 2. **类对象(Class Object)**:每个类都有一个对应的Class对象,它包含了...
动态加载类是Java反射机制的重要应用场景之一。通过`Class.forName()`方法或者`ClassLoader`的`loadClass()`方法,可以根据类名字符串在运行时加载类。 #### 七、操作成员 - **创建对象**:使用`Class`对象的`new...