http://blog.csdn.net/chaofanwei/article/details/12858523
1、classloader介绍
热部署,即需要jvm释放之前加载的业务class,且重新加载最新的业务class,并释放之前的class(卸载),其实类和普通对象一样都是对象,即如果从gc root除非,没有引用此类的别的对象存在,即会被jvm自动回收。
class文件在加载时,会把二进制文件放在内存中,并会在堆取new出一个表示此class的class对象,然后若new 类对象,则再把此new出来的对象的class对象指向刚才创建的class对象.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
类加载卸载图:
2、jdk自带classload介绍
还是直接看图吧
2.1类图
2.2 Classloader.loadClass(String name,boolean resolve)流程图
2.3 URLClassLoader.findClass(String name)流程图
3、实战
先贴代码:
两个项目,项目一用于搭建框架,实现热部署,项目二编写业务逻辑
项目一:
3.1、业务接口类
- package cn.myroute.server.service;
- public interface BusService {
- String doIt(String name);
- void close();
- }
3.2、自定义classload
- package cn.myroute.server;
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.util.Date;
- public class MyClassLoader extends URLClassLoader {
- private MyClassLoader loader = null;
- Date startDate = new Date();
- public MyClassLoader(URL[] urls) {
- super(urls);
- }
- public MyClassLoader(ClassLoader parent) {
- super(new URL[0], parent);
- }
- @Override
- public void close() throws IOException {
- // TODO Auto-generated method stub
- super.close();
- }
- /**
- * Adds a jar file from the filesystems into the jar loader list.
- *
- * @param jarfile
- * The full path to the jar file.
- * @throws MalformedURLException
- */
- public void addJarFile(String jarfile) throws MalformedURLException {
- URL url = new URL("file:" + jarfile);
- addURL(url);
- }
- public void addDir(String path) throws MalformedURLException{
- path= "file:"+path;
- URL url = new URL(path);
- addURL(url);
- }
- @Override
- public String toString() {
- // TODO Auto-generated method stub
- return super.toString() + ",time:"+startDate.toLocaleString();
- }
- }
3.3、热部署框架类
- package cn.myroute.server;
- import java.io.File;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.util.Date;
- import cn.myroute.server.service.BusService;
- public class Server {
- String codePath = "D:\\java\\workspace\\busservice\\bin\\";
- String busServiceClass="cn.myroute.server.impl.BusServiceImpl";
- BusService busService;
- public String doWork(String name){
- if(null != busService){
- return busService.doIt(name);
- }
- return "default";
- }
- public void init(){
- new Thread(){
- long lastTime=0;
- public void run() {
- File f = new File(codePath+"version.txt");
- while(true){
- if(lastTime != f.lastModified()){
- lastTime = f.lastModified();
- ClassLoader cl = this.getClass().getClassLoader();
- System.out.println(cl);
- MyClassLoader myLoader = new MyClassLoader(new URL[0]);
- try {
- myLoader.addDir(codePath);
- Class<BusService> clazz = (Class<BusService>) myLoader.loadClass(busServiceClass);
- BusService busService2 = clazz.newInstance();
- BusService temp = busService;
- busService = busService2;
- temp.close();//释放资源,尤其是线程,若线程不关闭的话,则类不会卸载,且会一直运行
- ClassLoader c = temp.getClass().getClassLoader();
- if(c instanceof URLClassLoader) ((URLClassLoader) c).close();//释放资源
- System.out.println("busService:"+busService + " ,classloader:"+busService.getClass().getClassLoader());
- System.out.println("end test "+ new Date().toLocaleString());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- };
- }.start();
- //myLoader.close();
- }
- }
只要codePath+"version.txt" 文件的修改时间发生变化,就会自动加载最新的业务类,并释放旧的类。
特别注意的是,释放旧类时,若旧类里面启动新的线程的话,一定要关闭,否则既不会释放旧类,而且线程会一直运行。虽然加载了最新的类,但旧类并没有释放,会导致内存占用和一些别的不可预估的问题。
3.4、客户端调用类
- package cn.myroute.client;
- import cn.myroute.server.Server;
- public class Client {
- public static void main(String[] args) {
- Server server = new Server();
- server.init();
- int i=0;
- while(true){
- i++;
- String name="name"+i;
- String result=server.doWork(name);
- System.out.println(result);
- try {
- Thread.sleep(1000* 5);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
3.5业务实现类
项目二:业务类
依赖项目一,因为需要业务接口类
- package cn.myroute.server.impl;
- import java.util.Date;
- import cn.myroute.server.service.BusService;
- public class BusServiceImpl implements BusService{
- boolean close = false;
- Date date= new Date();
- String version ="5.0";
- {
- Thread thread = new Thread(){
- public void run() {
- setName("buserviceimplThread:"+date.toLocaleString());
- System.out.println(this.toString() + ",time:"+date.toLocaleString());
- while(!close){
- doIt("");
- try {
- System.out.println(getName() + ",org time:"+date.toLocaleString() + " ,curtime:"+ new Date().toLocaleString());
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- };
- };
- thread.start();
- }
- @Override
- public String doIt(String name) {
- String res = version + name+",hello";
- return res;
- }
- @Override
- public void close() {
- close = true;
- System.out.println("close invoked !,"+this.toString() + ",time:"+date.toLocaleString());
- }
- }
运行时给jvm加 -verbose:class参数可以 打印类加载卸载信息
日志:
- buserviceimplThread:2016-5-2 21:45:17,org time:2016-5-2 21:45:17 ,curtime:2016-5-2 21:46:22
- 4.0name22,hello
- buserviceimplThread:2016-5-2 21:45:17,org time:2016-5-2 21:45:17 ,curtime:2016-5-2 21:46:27
- 4.0name23,hello
- buserviceimplThread:2016-5-2 21:45:17,org time:2016-5-2 21:45:17 ,curtime:2016-5-2 21:46:32
- sun.misc.Launcher$AppClassLoader@5c4e9b7d
- [Loaded cn.myroute.server.impl.BusServiceImpl from file:D:/java/workspace/busservice/bin/]
- [Loaded cn.myroute.server.impl.BusServiceImpl$1 from file:D:/java/workspace/busservice/bin/]
- close invoked !,cn.myroute.server.impl.BusServiceImpl@4f03a2d8,time:2016-5-2 21:45:17
- busService:cn.myroute.server.impl.BusServiceImpl@6010cc19 ,classloader:cn.myroute.server.MyClassLoader@68a124e5,time:2016-5-2 21:46:33
- end test 2016-5-2 21:46:33
- Thread[buserviceimplThread:2016-5-2 21:46:33,5,main],time:2016-5-2 21:46:33
- buserviceimplThread:2016-5-2 21:46:33,org time:2016-5-2 21:46:33 ,curtime:2016-5-2 21:46:33
- 5.0name24,hello
- buserviceimplThread:2016-5-2 21:46:33,org time:2016-5-2 21:46:33 ,curtime:2016-5-2 21:46:38
- buserviceimplThread:2016-5-2 21:46:33,org time:2016-5-2 21:46:33 ,curtime:2016-5-2 21:47:13
- 5.0name32,hello
- buserviceimplThread:2016-5-2 21:46:33,org time:2016-5-2 21:46:33 ,curtime:2016-5-2 21:47:18
- [Loaded sun.reflect.GeneratedMethodAccessor41 from __JVM_DefineClass__]
- <span style="color:#ff0000;">[Unloading class cn.myroute.server.impl.BusServiceImpl$1]
- [Unloading class cn.myroute.server.impl.BusServiceImpl]
- [Unloading class cn.myroute.server.impl.BusServiceImpl]
- [Unloading class cn.myroute.server.impl.BusServiceImpl$1]</span>
当内存占用很少的时候,需要手动通过工具如jconsole调用进程的gc,进行垃圾回收,才会打印出类卸载信息。
转载于:https://blog.csdn.net/chaofanwei2/article/details/51298818