Terry.Li-彬

          虛其心,可解天下之問;專其心,可治天下之學(xué);靜其心,可悟天下之理;恒其心,可成天下之業(yè)。

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            143 隨筆 :: 344 文章 :: 130 評(píng)論 :: 0 Trackbacks

          這篇文章主要是分析Tomcat中關(guān)于熱部署和JSP更新替換的原理,在此之前先介紹class的熱替換和class的卸載的原理。

          一 class的熱替換
          ClassLoader中重要的方法
          loadClass
          ????? ClassLoader.loadClass(...) 是ClassLoader的入口點(diǎn)。當(dāng)一個(gè)類沒有指明用什么加載器加載的時(shí)候,JVM默認(rèn)采用AppClassLoader加載器加載沒有加載過的class,調(diào)用的方法的入口就是loadClass(...)。如果一個(gè)class被自定義的ClassLoader加載,那么JVM也會(huì)調(diào)用這個(gè)自定義的ClassLoader.loadClass(...)方法來加載class內(nèi)部引用的一些別的class文件。重載這個(gè)方法,能實(shí)現(xiàn)自定義加載class的方式,拋棄雙親委托機(jī)制,但是即使不采用雙親委托機(jī)制,比如java.lang包中的相關(guān)類還是不能自定義一個(gè)同名的類來代替,主要因?yàn)镴VM解析、驗(yàn)證class的時(shí)候,會(huì)進(jìn)行相關(guān)判斷。
          ?
          defineClass
          ????? 系統(tǒng)自帶的ClassLoader,默認(rèn)加載程序的是AppClassLoader,ClassLoader加載一個(gè)class,最終調(diào)用的是defineClass(...)方法,這時(shí)候就在想是否可以重復(fù)調(diào)用defineClass(...)方法加載同一個(gè)類(或者修改過),最后發(fā)現(xiàn)調(diào)用多次的話會(huì)有相關(guān)錯(cuò)誤:
          ...
          java.lang.LinkageError
          attempted duplicate class definition
          ...
          所以一個(gè)class被一個(gè)ClassLoader實(shí)例加載過的話,就不能再被這個(gè)ClassLoader實(shí)例再次加載(這里的加載指的是,調(diào)用了defileClass(...)放方法,重新加載字節(jié)碼、解析、驗(yàn)證。)。而系統(tǒng)默認(rèn)的AppClassLoader加載器,他們內(nèi)部會(huì)緩存加載過的class,重新加載的話,就直接取緩存。所與對(duì)于熱加載的話,只能重新創(chuàng)建一個(gè)ClassLoader,然后再去加載已經(jīng)被加載過的class文件。www.2cto.com

          下面看一個(gè)class熱加載的例子:
          代碼:HotSwapURLClassLoader自定義classloader,實(shí)現(xiàn)熱替換的關(guān)鍵
          ? 1 package testjvm.testclassloader;
          ? 2
          ? 3 import java.io.File;
          ? 4 import java.io.FileNotFoundException;
          ? 5 import java.net.MalformedURLException;
          ? 6 import java.net.URL;
          ? 7 import java.net.URLClassLoader;
          ? 8 import java.util.HashMap;
          ? 9 import java.util.Map;
          ?10
          ?11 /**
          ?12? * 只要功能是重新加載更改過的.class文件,達(dá)到熱替換的作用
          ?13? * @author banana
          ?14? */
          ?15 public class HotSwapURLClassLoader extends URLClassLoader {
          ?16???? //緩存加載class文件的最后最新修改時(shí)間
          ?17???? public static Map<String,Long> cacheLastModifyTimeMap = new HashMap<String,Long>();
          ?18???? //工程class類所在的路徑
          ?19???? public static String projectClassPath = "D:/Ecpworkspace/ZJob-Note/bin/";
          ?20???? //所有的測(cè)試的類都在同一個(gè)包下
          ?21???? public static String packagePath = "testjvm/testclassloader/";
          ?22????
          ?23???? private static HotSwapURLClassLoader hcl = new HotSwapURLClassLoader();
          ?24????
          ?25???? public HotSwapURLClassLoader() {
          ?26???????? //設(shè)置ClassLoader加載的路徑
          ?27???????? super(getMyURLs());
          ?28???? }
          ?29????
          ?30???? public static HotSwapURLClassLoader? getClassLoader(){
          ?31???????? return hcl;
          ?32???? }
          ?33
          ?34???? private static? URL[] getMyURLs(){
          ?35???????? URL url = null;
          ?36???????? try {
          ?37???????????? url = new File(projectClassPath).toURI().toURL();
          ?38???????? } catch (MalformedURLException e) {
          ?39???????????? e.printStackTrace();
          ?40???????? }
          ?41???????? return new URL[] { url };
          ?42???? }
          ?43????
          ?44???? /**
          ?45????? * 重寫loadClass,不采用雙親委托機(jī)制("java."開頭的類還是會(huì)由系統(tǒng)默認(rèn)ClassLoader加載)
          ?46????? */
          ?47???? @Override
          ?48???? public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
          ?49???????? Class clazz = null;
          ?50???????? //查看HotSwapURLClassLoader實(shí)例緩存下,是否已經(jīng)加載過class
          ?51???????? //不同的HotSwapURLClassLoader實(shí)例是不共享緩存的
          ?52???????? clazz = findLoadedClass(name);
          ?53???????? if (clazz != null ) {
          ?54???????????? if (resolve){
          ?55???????????????? resolveClass(clazz);
          ?56???????????? }
          ?57???????????? //如果class類被修改過,則重新加載
          ?58???????????? if (isModify(name)) {
          ?59???????????????? hcl = new HotSwapURLClassLoader();
          ?60???????????????? clazz = customLoad(name, hcl);
          ?61???????????? }
          ?62???????????? return (clazz);
          ?63???????? }
          ?64
          ?65???????? //如果類的包名為"java."開始,則有系統(tǒng)默認(rèn)加載器AppClassLoader加載
          ?66???????? if(name.startsWith("java.")){
          ?67???????????? try {
          ?68???????????????? //得到系統(tǒng)默認(rèn)的加載cl,即AppClassLoader
          ?69???????????????? ClassLoader system = ClassLoader.getSystemClassLoader();
          ?70???????????????? clazz = system.loadClass(name);
          ?71???????????????? if (clazz != null) {
          ?72???????????????????? if (resolve)
          ?73???????????????????????? resolveClass(clazz);
          ?74???????????????????? return (clazz);
          ?75???????????????? }
          ?76???????????? } catch (ClassNotFoundException e) {
          ?77???????????????? // Ignore
          ?78???????????? }
          ?79???????? }
          ?80????????
          ?81???????? return customLoad(name,this);
          ?82???? }
          ?83
          ?84???? public Class load(String name) throws Exception{
          ?85???????? return loadClass(name);
          ?86???? }
          ?87
          ?88???? /**
          ?89????? * 自定義加載
          ?90????? * @param name
          ?91????? * @param cl
          ?92????? * @return
          ?93????? * @throws ClassNotFoundException
          ?94????? */
          ?95???? public Class customLoad(String name,ClassLoader cl) throws ClassNotFoundException {
          ?96???????? return customLoad(name, false,cl);
          ?97???? }
          ?98
          ?99???? /**
          100????? * 自定義加載
          101????? * @param name
          102????? * @param resolve
          103????? * @return
          104????? * @throws ClassNotFoundException
          105????? */
          106???? public Class customLoad(String name, boolean resolve,ClassLoader cl)
          107???????????? throws ClassNotFoundException {
          108???????? //findClass()調(diào)用的是URLClassLoader里面重載了ClassLoader的findClass()方法
          109???????? Class clazz = ((HotSwapURLClassLoader)cl).findClass(name);
          110???????? if (resolve)
          111???????????? ((HotSwapURLClassLoader)cl).resolveClass(clazz);
          112???????? //緩存加載class文件的最后修改時(shí)間
          113???????? long lastModifyTime = getClassLastModifyTime(name);
          114???????? cacheLastModifyTimeMap.put(name,lastModifyTime);
          115???????? return clazz;
          116???? }
          117????
          118???? public Class<?> loadClass(String name) throws ClassNotFoundException {
          119???????? return loadClass(name,false);
          120???? }
          121????
          122???? @Override
          123???? protected Class<?> findClass(String name) throws ClassNotFoundException {
          124???????? // TODO Auto-generated method stub
          125???????? return super.findClass(name);
          126???? }
          127????
          128???? /**
          129????? * @param name
          130????? * @return .class文件最新的修改時(shí)間
          131????? */
          132???? private long getClassLastModifyTime(String name){
          133???????? String path = getClassCompletePath(name);
          134???????? File file = new File(path);
          135???????? if(!file.exists()){
          136???????????? throw new RuntimeException(new FileNotFoundException(name));
          137???????? }
          138???????? return file.lastModified();
          139???? }
          140????
          141???? /**
          142????? * 判斷這個(gè)文件跟上次比是否修改過
          143????? * @param name
          144????? * @return
          145????? */
          146???? private boolean isModify(String name){
          147???????? long lastmodify = getClassLastModifyTime(name);
          148???????? long previousModifyTime = cacheLastModifyTimeMap.get(name);
          149???????? if(lastmodify>previousModifyTime){
          150???????????? return true;
          151???????? }
          152???????? return false;
          153???? }
          154????
          155???? /**
          156????? * @param name
          157????? * @return .class文件的完整路徑 (e.g. E:/A.class)
          158????? */
          159???? private String getClassCompletePath(String name){
          160???????? String simpleName = name.substring(name.lastIndexOf(".")+1);
          161???????? return projectClassPath+packagePath+simpleName+".class";
          162???? }
          163????
          164 }
          165

          代碼:Hot被用來修改的類
          1 package testjvm.testclassloader;
          2
          3 public class Hot {
          4???? public void hot(){
          5???????? System.out.println(" version 1 : "+this.getClass().getClassLoader());
          6???? }
          7 }
          8

          代碼:TestHotSwap測(cè)試類
          ?1 package testjvm.testclassloader;
          ?2
          ?3 import java.lang.reflect.Method;
          ?4
          ?5 public class TestHotSwap {
          ?6
          ?7???? public static void main(String[] args) throws Exception {
          ?8???????? //開啟線程,如果class文件有修改,就熱替換
          ?9???????? Thread t = new Thread(new MonitorHotSwap());
          10???????? t.start();
          11???? }
          12 }
          13
          14 class MonitorHotSwap implements Runnable {
          15???? // Hot就是用于修改,用來測(cè)試熱加載
          16???? private String className = "testjvm.testclassloader.Hot";
          17???? private Class hotClazz = null;
          18???? private HotSwapURLClassLoader hotSwapCL = null;
          19
          20???? @Override
          21???? public void run() {
          22???????? try {
          23???????????? while (true) {
          24???????????????? initLoad();
          25???????????????? Object hot = hotClazz.newInstance();
          26???????????????? Method m = hotClazz.getMethod("hot");
          27???????????????? m.invoke(hot, null); //打印出相關(guān)信息
          28???????????????? // 每隔10秒重新加載一次
          29???????????????? Thread.sleep(10000);
          30???????????? }
          31???????? } catch (Exception e) {
          32???????????? e.printStackTrace();
          33???????? }
          34???? }
          35
          36???? /**
          37????? * 加載class
          38????? */
          39???? void initLoad() throws Exception {
          40???????? hotSwapCL = HotSwapURLClassLoader.getClassLoader();
          41???????? // 如果Hot類被修改了,那么會(huì)重新加載,hotClass也會(huì)返回新的
          42???????? hotClazz = hotSwapCL.loadClass(className);
          43???? }
          44 }

          ???? 在測(cè)試類運(yùn)行的時(shí)候,修改Hot.class文件
          Hot.class
          原來第五行:System.out.println(" version 1 : "+this.getClass().getClassLoader());
          改后第五行:System.out.println(" version 2 : "+this.getClass().getClassLoader());
          ??
          輸出
          ?version 1 : testjvm.testclassloader.HotSwapURLClassLoader@610f7612
          ?version 1 : testjvm.testclassloader.HotSwapURLClassLoader@610f7612
          ?version 2 : testjvm.testclassloader.HotSwapURLClassLoader@45e4d960
          ?version 2 : testjvm.testclassloader.HotSwapURLClassLoader@45e4d960
          ???? 所以HotSwapURLClassLoader是重加載了Hot類 。注意上面,其實(shí)當(dāng)加載修改后的Hot時(shí),HotSwapURLClassLoader實(shí)例跟加載沒修改Hot的HotSwapURLClassLoader不是同一個(gè)。
          圖:HotSwapURLClassLoader加載情況

          ???? 總結(jié):上述類熱加載,需要自定義ClassLoader,并且只能重新實(shí)例化ClassLoader實(shí)例,利用新的ClassLoader實(shí)例才能重新加載之前被加載過的class。并且程序需要模塊化,才能利用這種熱加載方式。

          二 class卸載
          ????? 在Java中class也是可以u(píng)nload。JVM中class和Meta信息存放在PermGen space區(qū)域。如果加載的class文件很多,那么可能導(dǎo)致PermGen space區(qū)域空間溢出。引起:java.lang.OutOfMemoryErrorPermGen space.? 對(duì)于有些Class我們可能只需要使用一次,就不再需要了,也可能我們修改了class文件,我們需要重新加載 newclass,那么oldclass就不再需要了。那么JVM怎么樣才能卸載Class呢。

          ????? JVM中的Class只有滿足以下三個(gè)條件,才能被GC回收,也就是該Class被卸載(unload):
          ?? - 該類所有的實(shí)例都已經(jīng)被GC。
          ?? - 加載該類的ClassLoader實(shí)例已經(jīng)被GC。
          ?? - 該類的java.lang.Class對(duì)象沒有在任何地方被引用。

          ???? GC的時(shí)機(jī)我們是不可控的,那么同樣的我們對(duì)于Class的卸載也是不可控的。

          例子:
          代碼:SimpleURLClassLoader,一個(gè)簡(jiǎn)單的自定義classloader
          ? 1 package testjvm.testclassloader;
          ? 2
          ? 3 import java.io.File;
          ? 4 import java.net.MalformedURLException;
          ? 5 import java.net.URL;
          ? 6 import java.net.URLClassLoader;
          ? 7
          ? 8 public class SimpleURLClassLoader extends URLClassLoader {
          ? 9???? //工程class類所在的路徑
          ?10???? public static String projectClassPath = "E:/IDE/work_place/ZJob-Note/bin/";
          ?11???? //所有的測(cè)試的類都在同一個(gè)包下
          ?12???? public static String packagePath = "testjvm/testclassloader/";
          ?13????
          ?14???? public SimpleURLClassLoader() {
          ?15???????? //設(shè)置ClassLoader加載的路徑
          ?16???????? super(getMyURLs());
          ?17???? }
          ?18????
          ?19???? private static? URL[] getMyURLs(){
          ?20???????? URL url = null;
          ?21???????? try {
          ?22???????????? url = new File(projectClassPath).toURI().toURL();
          ?23???????? } catch (MalformedURLException e) {
          ?24???????????? e.printStackTrace();
          ?25???????? }
          ?26???????? return new URL[] { url };
          ?27???? }
          ?28????
          ?29???? public Class load(String name) throws Exception{
          ?30???????? return loadClass(name);
          ?31???? }
          ?32
          ?33???? public Class<?> loadClass(String name) throws ClassNotFoundException {
          ?34???????? return loadClass(name,false);
          ?35???? }
          ?36????
          ?37???? /**
          ?38????? * 重寫loadClass,不采用雙親委托機(jī)制("java."開頭的類還是會(huì)由系統(tǒng)默認(rèn)ClassLoader加載)
          ?39????? */
          ?40???? @Override
          ?41???? public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
          ?42???????? Class clazz = null;
          ?43???????? //查看HotSwapURLClassLoader實(shí)例緩存下,是否已經(jīng)加載過class
          ?44???????? clazz = findLoadedClass(name);
          ?45???????? if (clazz != null ) {
          ?46???????????? if (resolve){
          ?47???????????????? resolveClass(clazz);
          ?48???????????? }
          ?49???????????? return (clazz);
          ?50???????? }
          ?51
          ?52???????? //如果類的包名為"java."開始,則有系統(tǒng)默認(rèn)加載器AppClassLoader加載
          ?53???????? if(name.startsWith("java.")){
          ?54???????????? try {
          ?55???????????????? //得到系統(tǒng)默認(rèn)的加載cl,即AppClassLoader
          ?56???????????????? ClassLoader system = ClassLoader.getSystemClassLoader();
          ?57???????????????? clazz = system.loadClass(name);
          ?58???????????????? if (clazz != null) {
          ?59???????????????????? if (resolve)
          ?60???????????????????????? resolveClass(clazz);
          ?61???????????????????? return (clazz);
          ?62???????????????? }
          ?63???????????? } catch (ClassNotFoundException e) {
          ?64???????????????? // Ignore
          ?65???????????? }
          ?66???????? }
          ?67????????
          ?68???????? return customLoad(name,this);
          ?69???? }
          ?70
          ?71???? /**
          ?72????? * 自定義加載
          ?73????? * @param name
          ?74????? * @param cl
          ?75????? * @return
          ?76????? * @throws ClassNotFoundException
          ?77????? */
          ?78???? public Class customLoad(String name,ClassLoader cl) throws ClassNotFoundException {
          ?79???????? return customLoad(name, false,cl);
          ?80???? }
          ?81
          ?82???? /**
          ?83????? * 自定義加載
          ?84????? * @param name
          ?85????? * @param resolve
          ?86????? * @return
          ?87????? * @throws ClassNotFoundException
          ?88????? */
          ?89???? public Class customLoad(String name, boolean resolve,ClassLoader cl)
          ?90???????????? throws ClassNotFoundException {
          ?91???????? //findClass()調(diào)用的是URLClassLoader里面重載了ClassLoader的findClass()方法
          ?92???????? Class clazz = ((SimpleURLClassLoader)cl).findClass(name);
          ?93???????? if (resolve)
          ?94???????????? ((SimpleURLClassLoader)cl).resolveClass(clazz);
          ?95???????? return clazz;
          ?96???? }
          ?97????
          ?98???? @Override
          ?99???? protected Class<?> findClass(String name) throws ClassNotFoundException {
          100???????? return super.findClass(name);
          101???? }
          102 }
          103

          代碼:A
          1 public class A {?
          2 //? public static final Level CUSTOMLEVEL = new Level("test", 550) {}; // 內(nèi)部類
          3 }
          代碼:TestClassUnload,測(cè)試類
          ?1 package testjvm.testclassloader;
          ?2
          ?3 public class TestClassUnLoad {
          ?4
          ?5???? public static void main(String[] args) throws Exception {
          ?6???????? SimpleURLClassLoader loader = new SimpleURLClassLoader();
          ?7???????? // 用自定義的加載器加載A
          ?8???????? Class clazzA = loader.load("testjvm.testclassloader.A");
          ?9???????? Object a = clazzA.newInstance();
          10???????? // 清除相關(guān)引用
          11???????? a = null;
          12???????? clazzA = null;
          13???????? loader = null;
          14???????? // 執(zhí)行一次gc垃圾回收
          15???????? System.gc();
          16???????? System.out.println("GC over");
          17???? }
          18 }
          19

          ????? 運(yùn)行的時(shí)候配置VM參數(shù): -verbose:class;用于查看class的加載與卸載情況。如果用的是Eclipse,在Run Configurations中配置此參數(shù)即可。
          圖:Run Configurations配置???

          輸出結(jié)果
          .....
          [Loaded java.net.URI$Parser from E:\java\jdk1.7.0_03\jre\lib\rt.jar]
          [Loaded testjvm.testclassloader.A from file:/E:/IDE/work_place/ZJob-Note/bin/]
          [Unloading class testjvm.testclassloader.A]
          GC over
          [Loaded sun.misc.Cleaner from E:\java\jdk1.7.0_03\jre\lib\rt.jar]
          [Loaded java.lang.Shutdown from E:\java\jdk1.7.0_03\jre\lib\rt.jar]
          ......

          posted on 2013-03-05 17:16 禮物 閱讀(864) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。

          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 金秀| 普宁市| 正镶白旗| 志丹县| 侯马市| 安阳市| 夏邑县| 山丹县| 宽城| 扎鲁特旗| 黔江区| 中卫市| 新龙县| 都匀市| 宁南县| 中江县| 广南县| 资源县| 阿城市| 宜宾市| 鹤峰县| 南阳市| 盖州市| 河北省| 南溪县| 乌拉特中旗| 北海市| 河间市| 丰顺县| 福建省| 洱源县| 满洲里市| 巴塘县| 鹿泉市| 金湖县| 汝阳县| 儋州市| 平利县| 泸定县| 潮安县| 龙南县|