类加载机制之ClassLoader
1,类加载
每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Java代码经转换后的虚拟机指令,
当需要使用某个类时,虚拟机将会加载它的”.class”文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程称为类加载,这里我们需要了解一下类加载的过程,如下:
创建的对象存储在java 堆内存,对象的引用存储在java 虚拟机栈 class 对象中包含的方法,属性存储在方法区(JDK1.8 之前叫叫做永久区)
Jvm执行class文件

Loading:
step1: 类加载器将.class文件的二进制数据从外部存储器(如光盘,硬盘)调入内存中,在方法区生成方法区中的运行数据,生成java.lang.Class 对象(存储在java 堆)
step2: CPU再从内存中读取指令和数据进行运算,并将运算结果存入内存中(方法的返回结果也是存在方法区中) 。直接给CPU处理,而由于CPU的处理速度远远大于调入数据的速度,容易造成数据的脱节,所以需要内存起缓冲作用
每个类都对应有一个Class类型的对象,Class类的构造方法是私有的,只有JVM能够创建。因此Class对象是反射的入口,使用该对象就可以获得目标类所关联的.class文件中具体的数据结构。
Linking:
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
解析:虚拟机常量池的符号引用替换为字节引用过程
initilization:初始化
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
初始化过程:
类构造器<clinit>()方法是由编译器自动收藏类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生,代码从上往下执行。
也就是说这个方法,在类初始化阶段,会将类中所有的静态变量收集到一块并且分配空间,顺序是从上往下执行,若一个静态变量多次赋值,静态函数里面若没有类型也是可以的,会默认初始化:
static {
agent = 0;
}
private static int agent = 9;
2,类加载器常用方法
loadClass(String)
该方法加载指定名称(包括包名)的二进制类型,该方法在JDK1.2之后不再建议用户重写但用户可以直接调用该方法,loadClass()方法是ClassLoader类自己实现的,该方法中的逻辑就是双亲委派模式的实现,其源码如下,loadClass(String name, boolean resolve)是一个重载方法,resolve参数代表是否生成class对象的同时进行解析相关操作。
正如loadClass方法所展示的,当类加载请求到来时,先从缓存中查找该类对象,如果存在直接返回,如果不存在则交给该类加载去的父加载器去加载,倘若没有父加载则交给顶级启动类加载器去加载,最后倘若仍没有找到,则使用findClass()方法去加载(关于findClass()稍后会进一步介绍)。从loadClass实现也可以知道如果不想重新定义加载类的规则,也没有复杂的逻辑,只想在运行时加载自己指定的类,那么我们可以直接使用this.getClass().getClassLoder.loadClass("className"),这样就可以直接调用ClassLoader的loadClass方法获取到class对象。
findClass(String)
在JDK1.2之前,在自定义类加载时,总会去继承ClassLoader类并重写loadClass方法,从而实现自定义的类加载类,但是在JDK1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中,从前面的分析可知,findClass()方法是在loadClass()方法中被调用的,当loadClass()方法中父加载器加载失败后,则会调用自己的findClass()方法来完成类加载,这样就可以保证自定义的类加载器也符合双亲委托模式。需要注意的是ClassLoader类中并没有实现findClass()方法的具体代码逻辑,取而代之的是抛出ClassNotFoundException异常,同时应该知道的是findClass方法通常是和defineClass方法一起使用的(稍后会分析)
defineClass(byte[] b, int off, int len)
defineClass()方法是用来将byte字节流解析成JVM能够识别的Class对象(ClassLoader中已实现该方法逻辑),通过这个方法不仅能够通过class文件实例化class对象,也可以通过其他方式实例化class对象,如通过网络接收一个类的字节码,然后转换为byte字节流创建对应的Class对象,defineClass()方法通常与findClass()方法一起使用,一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象
resolveClass(Class≺?≻ c)
使用该方法可以使用类的Class对象创建完成也同时被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
3,热部署
对于Java应用程序来说,热部署就是在运行时更新Java类文件。
通俗的说,项目在运行过程中,已经部署到服务器的项目,发现逻辑有问题,需要修改代码,但是又不想在修改完毕之后重新部署,这时候就需要热部署的方法。
热部署就是直接修改.class 文件,但是修改之后,并不会生效,因为之前版本的.class 文件已经在项目启动的时候被类加载器读取到内存中,而且这个过程只会发生一次(除非重新部署),所以我们要实现热部署需要做的就是:
1,销毁之前的ClassLoader加载的对象,让System.gc(),提醒垃圾回收
2,更新字节码文件
3,创建新的自定义ClassLoader 读取字节码文件
Java热部署与热加载的联系
1.不重启服务器编译/部署项目
2.基于Java的类加载器实现
Java热部署与热加载的区别
部署方式
热部署在服务器运行时重新部署项目
热加载在运行时重新加载class
实现原理
热部署直接重新加载整个应用
热加载在运行时重新加载class
使用场景
热部署更多的是在生产环境使用
热加载则更多的实在开发环境使用
4,例子:
创建一个对象HelloWorld 类,里面打印Hello World 1,用记事本创建和HelloWorld类一样的java 文件,打印的是Hello World 2,Hello World2 的类用javac 编译,生成.class文件,在项目运行中先加载之前的class 文件,再用自定义的classLoader自动的去更新字节码文件,查看输出是否有变化。
package com.hella.hotswap;
public class HelloWorld {
public void search(){
System.out.println("Hello World 1");
}
}
创建对象:
package com.hella.hotswap;
public class HelloWorld {
public void search(){
System.out.println("Hello World 2");
}
}
//自定义ClassLoader
public class ClassLoaderDemo extends ClassLoader {
// name 是一个全路径 如 com.baidu.dev.HelloWorld
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
// 文件名称
String fileName = "/"+ name.replace(".", "/")+".class";
// 获取文件输入流
InputStream is = this.getClass().getResourceAsStream(fileName); //加 / 代表从classes 的根目录下开始找
// 读取字节
byte[] b = new byte[is.available()];
is.read(b);
// 将byte字节流解析成jvm能够识别的Class对象
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
}
测试:
import java.io.File;
import java.lang.reflect.Method; public class App {
public static void main(String[] args) throws Exception {
loadObject();
System.gc();
Thread.sleep(1000);// 等待资源回收
// 需要被热部署的class文件
File file1 = new File("C:\\Users\\caich5\\Desktop\\test\\HelloWorld.class");
// 之前编译好的class文件
File file2 = new File(
"C:\\Users\\caich5\\workspace\\JvmWeb\\target\\classes\\com\\hella\\hotswap\\HelloWorld.class");
boolean isDelete = file2.delete();// 删除旧版本的class文件
if (!isDelete) {
System.out.println("热部署失败.");
return;
}
file1.renameTo(file2);
System.out.println("update success!");
loadObject(); } private static void loadObject() throws Exception {
ClassLoaderDemo classLoaderDemo = new ClassLoaderDemo();
Class<?> clazz = classLoaderDemo.findClass("com.hella.hotswap.HelloWorld");
Object object = clazz.newInstance();
//通过反射机制获取方法
Method method = clazz.getMethod("search");
//反射机制对象执行方法
method.invoke(object); }
} 打印:
Hello World 1
update success!
Hello World 2
类加载机制之ClassLoader的更多相关文章
- Java 类加载机制 ClassLoader Class.forName 内存管理 垃圾回收GC
[转载] :http://my.oschina.net/rouchongzi/blog/171046 Java之类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指 ...
- 一文读懂类加载机制--ClassLoader
一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...
- Java编程的逻辑 (87) - 类加载机制
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- ClassLoader类加载机制
一.类加载器 类加载器(ClassLoader),顾名思义,即加载类的东西.在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘.网络或其他来源加载到内存中,并对字节码进行解 ...
- ClassLoader类加载机制&&JVM内存管理
一.ClassLoader类加载机制 在java中类加载是遵循委派双亲加载的:通过调用loadClass方法逐级往上传递委派加载请求,当找不到父ClassLoader时调用其findClass方法尝试 ...
- Java运行时环境---ClassLoader类加载机制
背景:听说ClassLoader类加载机制是进入BAT的必经之路. ClassLoader总述: 普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加 ...
- Spring 中的类加载机制 - ClassLoader
Spring 中的类加载机制 - ClassLoader Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) ClassLoa ...
- Java基础-类加载机制与自定义类Java类加载器(ClassLoader)
Java基础-类加载机制与自定义类Java类加载器(ClassLoader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于类加载器的概念和分类我就不再废话了,因为我在之前的笔 ...
- Java 类加载体系之 ClassLoader 双亲委托机制
Java 类加载体系之 ClassLoader 双亲委托机制 java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件 ...
随机推荐
- sql服务器第5级事务日志管理的阶梯:完全恢复模式下的日志管理
sql服务器第5级事务日志管理的阶梯:完全恢复模式下的日志管理 原文链接http://www.sqlservercentral.com/articles/Stairway+Series/73785/ ...
- ZYNQ自定义AXI总线IP应用——PWM实现呼吸灯效果
一.前言 在实时性要求较高的场合中,CPU软件执行的方式显然不能满足需求,这时需要硬件逻辑实现部分功能.要想使自定义IP核被CPU访问,就必须带有总线接口.ZYNQ采用AXI BUS实现PS和PL之间 ...
- 微信小程序转化为uni-app项目
前言: 之前自己做一个uni-app的项目的时候前端需要实现一个比较复杂的功能,但是由于自己前端抠脚的原因没有写出来,然后自己在网上搜索的时候发现了有个微信小程序里面的页面及其的符合我的需求.那么问题 ...
- java多线程基础API
本次内容主要讲认识Java中的多线程.线程的启动与中止.yield()和join.线程优先级和守护线程. 1.Java程序天生就是多线程的 一个Java程序从main()方法开始执行,然后按照既定的代 ...
- json中存整形数值,前端返回为空
实现目标: 在servlet中将整型数值转为json,然后在前端获取 问题描述: 前端获取为空,显示为:console.log打印为:{} 解决办法: 在servlet中将数值转为json格式再进行传 ...
- 《前端之路》 - 初试 TypeScript(一)基础数据类型
一.先讲讲 TypeScript 什么是 typeScript ? typeScript 是 Javascript 的超集 我们用一张图来简单介绍下 ts 和 js 清清楚楚明明白白的关系- 为什么会 ...
- js 统计一个字符串中出现的字符最多的字符
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 人见人爱A-B 题解
参加过上个月月赛的同学一定还记得其中的一个最简单的题目,就是{A}+{B},那个题目求的是两个集合的并集,今天我们这个A-B求的是两个集合的差,就是做集合的减法运算.(当然,大家都知道集合的定义,就是 ...
- (转)协议森林12 天下为公 (TCP堵塞控制)
协议森林12 天下为公 (TCP堵塞控制) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在TCP协议中,我们使用连接记录TCP ...
- yarn安装Electron提示安装正常却实际没有安装的解决
起因 使用Quasar框架开发Electron软件,配置好后发现electron没有正常安装,但是yarn却一本正经的胡说八道:不不不,electron已经在那里了.打开模块安装目录一看还真有elec ...