<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Terry.Li-彬

    虛其心,可解天下之問;專其心,可治天下之學;靜其心,可悟天下之理;恒其心,可成天下之業。

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      143 隨筆 :: 344 文章 :: 130 評論 :: 0 Trackbacks

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

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

    下面看一個class熱加載的例子:
    代碼:HotSwapURLClassLoader自定義classloader,實現熱替換的關鍵
    ? 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文件,達到熱替換的作用
    ?13? * @author banana
    ?14? */
    ?15 public class HotSwapURLClassLoader extends URLClassLoader {
    ?16???? //緩存加載class文件的最后最新修改時間
    ?17???? public static Map<String,Long> cacheLastModifyTimeMap = new HashMap<String,Long>();
    ?18???? //工程class類所在的路徑
    ?19???? public static String projectClassPath = "D:/Ecpworkspace/ZJob-Note/bin/";
    ?20???? //所有的測試的類都在同一個包下
    ?21???? public static String packagePath = "testjvm/testclassloader/";
    ?22????
    ?23???? private static HotSwapURLClassLoader hcl = new HotSwapURLClassLoader();
    ?24????
    ?25???? public HotSwapURLClassLoader() {
    ?26???????? //設置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,不采用雙親委托機制("java."開頭的類還是會由系統默認ClassLoader加載)
    ?46????? */
    ?47???? @Override
    ?48???? public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
    ?49???????? Class clazz = null;
    ?50???????? //查看HotSwapURLClassLoader實例緩存下,是否已經加載過class
    ?51???????? //不同的HotSwapURLClassLoader實例是不共享緩存的
    ?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."開始,則有系統默認加載器AppClassLoader加載
    ?66???????? if(name.startsWith("java.")){
    ?67???????????? try {
    ?68???????????????? //得到系統默認的加載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()調用的是URLClassLoader里面重載了ClassLoader的findClass()方法
    109???????? Class clazz = ((HotSwapURLClassLoader)cl).findClass(name);
    110???????? if (resolve)
    111???????????? ((HotSwapURLClassLoader)cl).resolveClass(clazz);
    112???????? //緩存加載class文件的最后修改時間
    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文件最新的修改時間
    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????? * 判斷這個文件跟上次比是否修改過
    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測試類
    ?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就是用于修改,用來測試熱加載
    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); //打印出相關信息
    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類被修改了,那么會重新加載,hotClass也會返回新的
    42???????? hotClazz = hotSwapCL.loadClass(className);
    43???? }
    44 }

    ???? 在測試類運行的時候,修改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類 。注意上面,其實當加載修改后的Hot時,HotSwapURLClassLoader實例跟加載沒修改Hot的HotSwapURLClassLoader不是同一個。
    圖:HotSwapURLClassLoader加載情況

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

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

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

    ???? GC的時機我們是不可控的,那么同樣的我們對于Class的卸載也是不可控的。

    例子:
    代碼:SimpleURLClassLoader,一個簡單的自定義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???? //所有的測試的類都在同一個包下
    ?12???? public static String packagePath = "testjvm/testclassloader/";
    ?13????
    ?14???? public SimpleURLClassLoader() {
    ?15???????? //設置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,不采用雙親委托機制("java."開頭的類還是會由系統默認ClassLoader加載)
    ?39????? */
    ?40???? @Override
    ?41???? public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
    ?42???????? Class clazz = null;
    ?43???????? //查看HotSwapURLClassLoader實例緩存下,是否已經加載過class
    ?44???????? clazz = findLoadedClass(name);
    ?45???????? if (clazz != null ) {
    ?46???????????? if (resolve){
    ?47???????????????? resolveClass(clazz);
    ?48???????????? }
    ?49???????????? return (clazz);
    ?50???????? }
    ?51
    ?52???????? //如果類的包名為"java."開始,則有系統默認加載器AppClassLoader加載
    ?53???????? if(name.startsWith("java.")){
    ?54???????????? try {
    ?55???????????????? //得到系統默認的加載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()調用的是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) {}; // 內部類
    3 }
    代碼:TestClassUnload,測試類
    ?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???????? // 清除相關引用
    11???????? a = null;
    12???????? clazzA = null;
    13???????? loader = null;
    14???????? // 執行一次gc垃圾回收
    15???????? System.gc();
    16???????? System.out.println("GC over");
    17???? }
    18 }
    19

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

    輸出結果
    .....
    [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 禮物 閱讀(860) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。

    網站導航:
     
    主站蜘蛛池模板: 51午夜精品免费视频| 国产区在线免费观看| 国产免费人成视频在线观看| 立即播放免费毛片一级| www.亚洲精品| 久久99精品视免费看| 一本天堂ⅴ无码亚洲道久久| 免费在线观看你懂的| 久久综合国产乱子伦精品免费 | 亚洲精品在线电影| 国产无遮挡吃胸膜奶免费看视频| 一边摸一边桶一边脱免费视频| 亚洲第一精品福利| 午夜国产羞羞视频免费网站| 秋霞人成在线观看免费视频| 亚洲精品第一国产综合亚AV| 国产亚洲成AV人片在线观黄桃| 青青草免费在线视频| 国产线视频精品免费观看视频| 亚洲一级毛片免费在线观看| 青青草原亚洲视频| 免费黄色大片网站| 免费观看激色视频网站bd | 亚洲自偷自拍另类图片二区| 亚洲综合精品网站| 最近中文字幕mv手机免费高清| 十八禁视频在线观看免费无码无遮挡骂过 | 永久免费精品影视网站| 亚洲图片激情小说| 91亚洲国产成人精品下载| 亚洲福利视频一区二区| 在线日韩av永久免费观看| 免费国产成人高清在线观看网站| 可以免费观看的毛片| 一道本在线免费视频| 亚洲AV无码AV日韩AV网站| 亚洲一卡2卡4卡5卡6卡在线99| 成年性午夜免费视频网站不卡| 久久久WWW免费人成精品| 亚洲视频在线一区二区三区| 国产精一品亚洲二区在线播放|