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

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

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

    豬兒笨笨的文檔

    主要是個人的一些思考和技術文章,還有許多翻譯的文檔

     

    Wicket1.3中Class熱加載--揭秘篇

     

    在文章《Wicket1.3中Class熱加載--使用篇》中,展示了如何使用Wicket1.3提供的ReloadingWicketFilter來動態加載修改后的類(包括修改了簽名的類),從而實現高效開發。但在該文章中,只是給出了如何使用該功能的說明,而本篇文章將明確解析Wicket1.3類熱加載魔法的奧秘所在。

    在上一篇文章中,是通過修改Wicket項目中web.xml文件,將其中的

    org.apache.wicket.protocol.http.WicketFilter

    全部替換成

    org.apache.wicket.protocol.http.ReloadingWicketFilter

    從而開啟了Wicket類熱加載的功能。那么為了探究其魔法奧秘,入手點自然就選擇ReloadingWicketFilter這個類了。

    先來看一下ReloadingWicketFilter的代碼,驚人的少:

    public class ReloadingWicketFilter extends WicketFilter

    {

    private ReloadingClassLoader reloadingClassLoader;

    /**

     * Instantiate the reloading class loader

     */

    public ReloadingWicketFilter()

    {

    // Create a reloading classloader

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    }

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#getClassLoader()

     */

    protected ClassLoader getClassLoader()

    {

    return reloadingClassLoader;

    }

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#init(javax.servlet.FilterConfig)

     */

    public void init(final FilterConfig filterConfig) throws ServletException

    {

    reloadingClassLoader.setListener(new IChangeListener()

    {

    public void onChange()

    {

    // Remove the ModificationWatcher from the current reloading class loader

    reloadingClassLoader.destroy();

    /*

     * Create a new classloader, as there is no way to clear a ClassLoader's cache. This

     * supposes that we don't share objects across application instances, this is almost

     * true, except for Wicket's Session object.

     */

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    try

    {

    init(filterConfig);

    }

    catch (ServletException e)

    {

    Throw new RuntimeException(e);

    }

    }

    });

    super.init(filterConfig);

    }

    }

    其中最引人注目的就是那個ReloadingClassLoader,再打開WicketFilter類中,很容易找到以下代碼,它表示使用自定義的ClassLoader來加載類。

    final ClassLoader newClassLoader = getClassLoader();

    Thread.currentThread().setContextClassLoader(newClassLoader);

    而ReloadingWicketFilter則是重載了getClassLoader方法,以返回了自定義的ReloadingClassLoader。也就是說Wicket的魔法其實是使用了一個自定義的ReloadingClassLoader來實現類的熱加載(包括對簽名被修改的類)。為了讓大家更清楚理解Wicket這一方法以,在分析ReloadingClassLoader之前,先來簡單的過一下JVM的類加載機制。

    在JDK1.2以后,JVM在加載類時默認采用的是雙親委機制(早期的類加載機制存在安全漏洞)。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器,依次遞歸,所有 ClassLoaders 的根是系統 ClassLoader,它會以缺省方式裝入類,本地文件系統加載(可能是Jar包,也可能是Class文件),如果父類加載器可以完成類加載任務,就成功返回加載后的類但如果父類加載器無法完成此加載任務時,那么這個特定的類加載才自己去加載指定名稱的類。這樣的雙親委機制可以保證象java.io.*這種基礎類庫的內容一定是被系統ClassLoader加載,從而保證類加載的安全性。

    下面是雙親委派機制的示意圖:

    實例分析Web應用下的類加載順序:

    為了更好的方便大家理解類的加載機制,并說明Wicket如何使用自定義的ClassLoader來加載更改后的類,下面將有一個簡單的實例來說明。

    首先將前一篇文章中的HelloWorld代碼修改為:

    public class HelloWorld extends WicketExamplePage

    {

    /**

     * Constructor

     */

    public HelloWorld()

    {

    ClassLoader classLoader = this.getClass().getClassLoader();

    while (null != classLoader) 

    {

    System.err.println("loader   " + classLoader.hashCode()+"   "+classLoader.getClass());

    classLoader = classLoader.getParent();

    }

    add(new Label("message""New Hello World!"));

    }

    }

    修改以后的代碼,可以在對象被創建時,輸出HelloWorld類的ClassLoader及其父ClassLoader,接下來恢復web.xml文件中的filter為WicketFilter,不使用RelodingWicketFilter,從而觀察原先的Class加載順序,得到的結果為:

    loader   2011334   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    接下來仍然按照上一篇文章中的操作修改web.xml,使用RelodingWicketFilter,開啟Wicket類的熱加載功能。再看一下輸出結果:

    loader   8310913   class org.apache.wicket.application.ReloadingClassLoader

    loader   2011334   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    可見通過代碼

    final ClassLoader newClassLoader = getClassLoader();

    Thread.currentThread().setContextClassLoader(newClassLoader);

    ReloadingWicketFilter使用ReloadingClassLoader作為當前類的ClassLoader,也就是說它接管了所有WEB-INF/classes目錄下面類文件的加載。這樣它就可以根據實際情況來加載一個類。但有經驗的程序員都有知道,一般來說Class一旦被加載,就表示在整個JVM生命周期的過程中,不會自動釋放,而是放置在內存中。那么Wicket又是怎么釋放已經加載的類,同時加載修改后的類呢?看一段ReloadingWicketFilter中init方法的代碼:

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#init(javax.servlet.FilterConfig)

     */

    public void init(final FilterConfig filterConfig) throws ServletException

    {

    reloadingClassLoader.setListener(new IChangeListener()

    {

    public void onChange()

    {

    // Remove the ModificationWatcher from the current reloading class loader

    reloadingClassLoader.destroy();

    /*

     * Create a new classloader, as there is no way to clear a ClassLoader's cache. This

     * supposes that we don't share objects across application instances, this is almost

     * true, except for Wicket's Session object.

     */

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    try

    {

    init(filterConfig);

    }

    catch (ServletException e)

    {

    Throw new RuntimeException(e);

    }

    }

    });

    super.init(filterConfig);

    }

    這段代碼表示,一旦發現類文件的改變,將會銷毀當前的reloadingClassLoader ,同時新建一個ReloadingClassLoader的實例,因為ClassLoader被銷毀了,所以由該ClassLoader加載的類都將被銷毀,然后再由新的ReloadingClassLoader進行加載,因此即使是修改了簽名的類也是可以被正確加載成功的。當然這種做法,可能會引起Session中的數據不能正確識別和轉換。但相對于開發環境下,開發人員通過另起一個新的Session就可以開始正常的工作了,還是可以有效的提高開發效率。

    Wicket類加載的一個潛在問題:

    如果僅僅使用Wicket開發程序,那么Wicket1.3引入的熱加載機制,對開發人員來說,會是一件非常幸福的事情,但如果同時使用了jsp,也就是說同時在一個Web應用程序(不是Web應用服務器)中同時使用Wicket和JSP,而且在Wicket和JSP代碼分別向Session中寫入相同的對象,如用戶信息之類的數據對象,那么就會出現一些不必要的問題,最覺的莫過于ClassCastException。雖然共用Wicket+JSP的情況比較少,出問題的機率也比較少,但還是要額外提出來作為一個警示。

    下面是一個簡單的類Person代碼,用來展示如何出現ClassCastException:

    public class Person 

    {

    /**

     * Default constructor

     */

    public Person() 

    {

    super();

    ClassLoader classLoader = this.getClass().getClassLoader();

    while (null != classLoader) 

    {

    System.err.println("loader   " + classLoader.hashCode()+"   "+classLoader.getClass());

    classLoader = classLoader.getParent();

    }

    }

    }

    在構造函數中的那段代碼,可以輸出它的ClassLoader順序,接下來象先前一樣訪問那個HelloWorld應用,得到如下的ClassLoader順序:

    loader   8310913   class org.apache.wicket.application.ReloadingClassLoader

    loader   3862294   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    然后再寫一個run.jsp,代碼如下:

    <%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

    <%@page import="org.apache.wicket.examples.Person"%>

    <html>

    <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <title>Class Loader Demo</title>

    </head>

    <body>

    <%

    new Person();

    %>

    </body>

    </html>

    在run.jsp中,初始化一個Person對象,同樣觀察它的輸出結果,得到另外一個不同的Class加載順序:

    loader   3862294   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    可見在JSP中的Person與在HelloWorld中的Person是由不同的ClassLoader加載的(JSP編譯成Servlet執行,在Tomcat中,org.apache.jasper.servlet.JasperLoader負責JSP編譯后的類加載),根據JVM規范,這兩個Class是不等價的。因此進行轉換的時候,會引起ClassCastException,這是特別需要注意的一點。


    點擊這里下載Word格式

    posted on 2008-11-24 11:44 豬兒笨笨 閱讀(1722) 評論(3)  編輯  收藏 所屬分類: Java開發 、組件設計 、開源軟件 、Wicket

    評論

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄] 2008-11-25 10:18 beansoft

    Create a new classloader, as there is no way to clear a ClassLoader's cache.

    類加載器里面的類信息是無法被清理掉的, 這也是為什么reload幾次應用就會出現OOM的原因, 個人認為是每個類的.class對象都無法被垃圾回收, 例如Spring+Hibernate會動態生成N多類, 很容易導致perm溢出問題.
    最近在看JavaRebel, 對免費類熱加載很感興趣, 因此打開了一下 ReloadingClassLoader 的源碼, 其destory()方法的實現的確沒有辦法清除類信息:
    /**
    * Remove the ModificationWatcher from the current reloading class loader
    */
    public void destroy()
    {
    watcher.destroy();
    }  回復  更多評論   

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄] 2008-11-25 10:18 beansoft

    感謝提供好信息!! 不錯, 您研究的夠深入!  回復  更多評論   

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄] 2008-11-25 12:14 豬兒笨笨

    是否可以被清除,這個其實在JVM中應該是沒有規定的
    當然Real JVM可能有所規定
    但我估計對于自動GC機制,很難準確分析哪些類沒有被占用,或者即使分析成本也比較高,所以常見的JVM是沒有處理的。
    雖然沒有能夠釋放,但是對于開放人員來講,這個功能還是非常實用的。
    所以我在上篇中也說,這個功能在開發時用用OK,千萬不要在上線系統中用。  回復  更多評論   

    導航

    統計

    常用鏈接

    留言簿(18)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲一区精品无码| 亚洲精品视频免费| 亚洲欧洲免费视频| 四虎永久在线免费观看| 成人午夜免费福利视频| 最近2019中文免费字幕在线观看 | 久久香蕉国产线看免费| 国产精品亚洲二区在线| 亚洲乱人伦精品图片| 亚洲AV综合色一区二区三区| 亚洲国产日韩成人综合天堂| 午夜爱爱免费视频| 国产情侣激情在线视频免费看| 在线毛片片免费观看| 久久精品成人免费观看97| 校园亚洲春色另类小说合集 | 亚洲免费福利在线视频| 免费看又黄又无码的网站| a级精品九九九大片免费看| 一级看片免费视频| 黄色免费在线网址| 久久亚洲AV成人无码国产最大| 精品日韩99亚洲的在线发布| 亚洲人成电影青青在线播放| 91在线精品亚洲一区二区| 亚洲欧洲日产国产综合网| 亚洲国产精品久久66| 亚洲网站在线观看| 亚洲精品综合一二三区在线| 久久久久亚洲精品无码系列| 久久91亚洲精品中文字幕| 国产亚洲综合一区柠檬导航| 国产亚洲人成网站在线观看不卡| 九月丁香婷婷亚洲综合色| 亚洲va无码专区国产乱码| 久久亚洲精品视频| 99人中文字幕亚洲区| 亚洲精品美女在线观看| 亚洲一卡二卡三卡四卡无卡麻豆| 亚洲无码一区二区三区| 亚洲欧美日韩久久精品|