jvm之classLoader
背景:
classLoader:类加载器。大家平时在开发工具编写的代码会保存为.java文件,然后使用工具(javac命令等)把.java文件编译为.class文件。classLoder是专门查找,加载和解析.class文件。
在JVM中有三种classLoader,这三种ClassLoader都是有层级关系,并且三种ClassLoader加载.class和文件目录是有分工的
JVM classLoader的分类
Ø 第一种:bootstrapClassLoader
bootstrapClassLoader,据说是jdk底层用C++实现的引导类加载器,它主要负责加载%JAVA_HOME%/jre/lib和-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。比如rt.jar(包含了很多JSE中的api对应的class),在jvm运行时,如果通过class.getClassLoader方法返回null,则表示该类是被bootstrapClassLoader加载的
Ø 第二种:extendClassLoader
extendClassLoader, java实现的一个classLoader(具体实现是做为sun.misc.Launcher内部实现类extendClassLoader),主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。比如dnsns.jar中的class
Ø 第三种:AppClassLoader
AppClassLoader,用java实现的一个ClassLoader(具体实现是做为sun.misc.Launcher内部实现类AppClassLoader),主要加载应用中的class,也就是程序员写的.java编译成的.class
ClassLoader的特点:
Ø classLoader加载机制是双亲委托机制,比如AppClassLoader要加载A.class,首先判断自己有没有加载过,如果没有加载过,则会委托父加载器也就是extendClassLoader进行加载,如果父类加载器返回null,不加载,则在用自己加载器
Ø 2、同一个classLoader实例,对每个class只有加载一次,不会重复加载,比如AppClassLoader加载了A.class,如果再调用AppClassLoader.loadClass方法加载A.class,则会返回自己缓存中已经加载过的A.class
打破classLoader的特性
classLoader的特点并不是一层不变,不能打破的。为了实现某些功能,或者为了实现某个业务不得不牺牲既定的规则,business firest,technology second。jvm也会打包既定的规则。
Ø 打破规则1双亲委托加载
大家知道java中定义了很多JNDI的接口,但是接口具体的实现却是由不同的厂家提供,这样就会出现个现象,因为JNDI的接口都是jdk提供的,也就是接口所在的class会被bootStrap加载,但是厂家的实现类,基本都放到了相应应用中,会被appClassLoader进行加载。JNDI接口在启动时,需要调用相应厂家的实现类,而这时会出现bootstrapClassLoader不能加载具体实现类(bootstrapClassLoader加载特点和路径决定了),为了解决这个问题,java设计团队提出了线程上下文类加载器(可以通过Thread.currentThread().getContextClassLoader()获得)。该加载器可以通过setContentClassLoader进行设置,如果没有设置过,则会使用父线程的classLoader,如果在应用程序都没有设置过,默认为appClassLoader。
有了线程上下文classLoader,就比较好实现此功能了,在加载JNDI接口的代码里,通过线程上下文ClassLoader拿到AppClassLoader,加载JNDI具体的实现类,也就是bootStrapClassLoader,会调用appClassLoader进行实现类的加载。通过这样打破了双亲委托加载
Ø 打破规则2 同一个类只会被classLoader加载一次。同一个类可以被classLoader多次加载到jvm中
热代码替换:比如A.class修改后,可以直接加载到jvm内存中。主要实现思路为A.class的加载由自定义实现的ClassLoader进行类加载,主要是自定义的ClassLoader继承ClassLoader,自己读取A.class的信息,然后调用defineClass方法,让jvm计算出该class的内存信息
图
具体java代码:
import java.util.Map; public interface IConfig { public String getConfig(Map<String, String> params); }
自定义classLoader
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class IConfigImplClassLoaderUtil { public IConfig getConfiObject(String classUrl){ ClassLoader classLoader = new ClassLoader() { @Override protected Class<?> findClass(String className){ System.out.println("==========findClass======"); byte[] classBuffer = loadClassBuffer(className); return defineClass(null, classBuffer, 0, classBuffer.length); } private byte[] loadClassBuffer(String className) { return getClassBytesFromFile(className); }; private byte[] getClassBytesFromFile(String className){ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(className); byte[] buffer = new byte[1024]; int length = -1; while((length = fileInputStream.read(buffer)) > -1){ byteArrayOutputStream.write(buffer, 0, length); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(null != fileInputStream){ try { fileInputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return byteArrayOutputStream.toByteArray(); } }; try { Class wifiClass = classLoader.loadClass(classUrl); return (IConfig) wifiClass.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static void main(String[] args) { String classUrl = "D:\\TestConfig.class"; IConfigImplClassLoaderUtil classLoaderUtil = new IConfigImplClassLoaderUtil(); IConfig wifiConfig = classLoaderUtil.getConfiObject(classUrl); Map<String, String> params = new HashMap<String, String>(); params.put("param1", "jack_liu"); params.put("valu1", "test1234567890"); String result = wifiConfig.getConfig(params); System.out.println("result:" + result); //修改TestConfig.java,重新编译再把class放到D盘 IConfig wifiConfig1 = classLoaderUtil.getConfiObject(classUrl); result = wifiConfig1.getConfig(params); System.out.println("result:" + result); System.out.println(wifiConfig1 instanceof IConfig); System.out.println(wifiConfig1.getClass().getClassLoader()); System.out.println(IConfig.class.getClassLoader()); Thread.currentThread().getContextClassLoader(); } }
注意:如果需要实现上面的例子,需要实现Iconfig接口
参考资料:http://my.oschina.net/aminqiao/blog/262601#OSC_h1_7
《深入理解java 虚拟机》
相关推荐
JVM ClassLoader简析.压缩包中文档和示例代码
探索JVM底层奥秘ClassLoader源码分析与案例讲解,探索JVM底层奥秘ClassLoader源码分析与案例讲解.
运用代理模式,通过自定义classloader对代码加密,啊;敌法;打飞机
讲解JVM的ClassLoader子系统原理.
JVM初探- 内存分配、GC原理与垃圾收集器,从从提上讲解了jvm中GC的原理、基本的算法和针对不同内存区使用的算法,同时,详细的讲解了当前主要使用的垃圾收集器
深入Java虚拟机JVM类加载学习笔记:jvm java classloader 垃圾回收 gc
ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的
• JVM指令介绍,获得ClassLoader的途径,CAS指令由硬件提供 • 并发程序设计实现的基础 • 486之后并不需要锁总线 • 基于MESI缓存⼀一致性协议 如果不声明volatile,变量装载到本地变量 中,或者cpu cache中,多...
目前,Java是最为流行的编程语言之一,它的基础平台就是JVM。除了Java,如JRuby、Scala、Clojure等语言也运行在JVM平台。熟悉和掌握JVM平台有着重要的实用价值和意义。 在本课程中个,将详细介绍JVM的基本原理、...
JVM初探内存分配GC原理与垃圾收集器共16页.pdf.zip
jvm 配置jvm参数 配置jvm参数
jvm源码
NULL 博文链接:https://caerun.iteye.com/blog/1162634
JVM内存模型,类加载模式工作机制详细,内存屏障,类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三...
jvm源码,jvm-native的源码,jvm支行机制,可对jvm的运行过程进行分析 个人网站:https://www.zhangjunbk.com
微信小程序详细图文教程 泉州大白网络科技 目录 一.微信小程序申请 二....1.申请服务器 2.部署服务器 3.域名申请和配置 三....一....申请,并认证(未认证不能发布,认证需要300元,目前只支持企业认证)详细见官网说明。...
java基础之JVM,jvm是java的核心技术,也是面试过程中经常出现的
JVM 内存管理之道 JVM垃圾回收机制 JVM GC组合 JVM 内存监控工具
JVM每次装入类文件时都需要一个称为ClassLoader的对象,这个对象负责把新的类装入正在运行的JVM。JVM给ClassLoader一个包含了待装入类名字的字符串,然后由ClassLoader负责找到类文件,装入原始数据,并把它转换成一...