免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
class卸載、熱替換和Tomcat的熱部署的分析
所以一個(gè)class被一個(gè)ClassLoader實(shí)例加載過的話,就不能再被這個(gè)ClassLoader實(shí)例再次加載(這里的加載指的是,調(diào)用了defileClass(...)放方法,重新加載字節(jié)碼、解析、驗(yàn)證。)。而系統(tǒng)默認(rèn)的AppClassLoader加載器,他們內(nèi)部會緩存加載過的class,重新加載的話,就直接取緩存。所與對于熱加載的話,只能重新創(chuàng)建一個(gè)ClassLoader,然后再去加載已經(jīng)被加載過的class文件。

下面看一個(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     //所有的測試的類都在同一個(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."開頭的類還是會由系統(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測試類
 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); //打印出相關(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類被修改了,那么會重新加載,hotClass也會返回新的
42         hotClazz = hotSwapCL.loadClass(className);
43     }
44 }

     在測試類運(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也是可以unload。JVM中class和Meta信息存放在PermGen space區(qū)域。如果加載的class文件很多,那么可能導(dǎo)致PermGen space區(qū)域空間溢出。引起:java.lang.OutOfMemoryErrorPermGen space.  對于有些Class我們可能只需要使用一次,就不再需要了,也可能我們修改了class文件,我們需要重新加載 newclass,那么oldclass就不再需要了。那么JVM怎么樣才能卸載Class呢。

      JVM中的Class只有滿足以下三個(gè)條件,才能被GC回收,也就是該Class被卸載(unload):

   - 該類所有的實(shí)例都已經(jīng)被GC。
   - 加載該類的ClassLoader實(shí)例已經(jīng)被GC。
   - 該類的java.lang.Class對象沒有在任何地方被引用。


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

例子:
代碼:SimpleURLClassLoader,一個(gè)簡單的自定義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     //所有的測試的類都在同一個(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."開頭的類還是會由系統(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,測試類
 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]
......
      上面輸出結(jié)果中的確A.class被加載了,然后A.class又被卸載了。這個(gè)例子中說明了,即便是class加載進(jìn)了內(nèi)存,也是可以被釋放的。

圖:程序運(yùn)行中,引用沒清楚前,內(nèi)存中情況

圖:垃圾回收后,程序沒結(jié)束前,內(nèi)存中情況
 

    1、有啟動類加載器加載的類型在整個(gè)運(yùn)行期間是不可能被卸載的(jvm和jls規(guī)范).
    2、被系統(tǒng)類加載器和標(biāo)準(zhǔn)擴(kuò)展類加載器加載的類型在運(yùn)行期間不太可能被卸載,因?yàn)橄到y(tǒng)類加載器實(shí)例或者標(biāo)準(zhǔn)擴(kuò)展類的實(shí)例基本上在整個(gè)運(yùn)行期間總能直接或者間接的訪問的到,其達(dá)到unreachable的可能性極小.(當(dāng)然,在虛擬機(jī)快退出的時(shí)候可以,因?yàn)椴还蹸lassLoader實(shí)例或者Class(java.lang.Class)實(shí)例也都是在堆中存在,同樣遵循垃圾收集的規(guī)則).
    3、被開發(fā)者自定義的類加載器實(shí)例加載的類型只有在很簡單的上下文環(huán)境中才能被卸載,而且一般還要借助于強(qiáng)制調(diào)用虛擬機(jī)的垃圾收集功能才可以做到.可以預(yù)想,稍微復(fù)雜點(diǎn)的應(yīng)用場景中(尤其很多時(shí)候,用戶在開發(fā)自定義類加載器實(shí)例的時(shí)候采用緩存的策略以提高系統(tǒng)性能),被加載的類型在運(yùn)行期間也是幾乎不太可能被卸載的(至少卸載的時(shí)間是不確定的).
      綜合以上三點(diǎn), 一個(gè)已經(jīng)加載的類型被卸載的幾率很小至少被卸載的時(shí)間是不確定的.同時(shí),我們可以看的出來,開發(fā)者在開發(fā)代碼時(shí)候,不應(yīng)該對虛擬機(jī)的類型卸載做任何假設(shè)的前提下來實(shí)現(xiàn)系統(tǒng)中的特定功能.
       
三 Tomcat中關(guān)于類的加載與卸載
        Tomcat中與其說有熱加載,還不如說是熱部署來的準(zhǔn)確些。因?yàn)閷τ谝粋€(gè)應(yīng)用,其中class文件被修改過,那么Tomcat會先卸載這個(gè)應(yīng)用(Context),然后重新加載這個(gè)應(yīng)用,其中關(guān)鍵就在于自定義ClassLoader的應(yīng)用。這里有篇文章很好的介紹了tomcat中對于ClassLoader的應(yīng)用,請點(diǎn)擊here。

Tomcat啟動的時(shí)候,ClassLoader加載的流程:
1 Tomcat啟動的時(shí)候,用system classloader即AppClassLoader加載{catalina.home}/bin里面的jar包,也就是tomcat啟動相關(guān)的jar包。
2 Tomcat啟動類Bootstrap中有3個(gè)classloader屬性,catalinaLoader、commonLoader、sharedLoader在Tomcat7中默認(rèn)他們初始化都為同一個(gè)StandardClassLoader實(shí)例。具體的也可以在{catalina.home}/bin/bootstrap.jar包中的catalina.properites中進(jìn)行配置。
3 StandardClassLoader加載{catalina.home}/lib下面的所有Tomcat用到的jar包。
4 一個(gè)Context容器,代表了一個(gè)app應(yīng)用。Context-->WebappLoader-->WebClassLoader。并且Thread.contextClassLoader=WebClassLoader。應(yīng)用程序中的jsp文件、class類、lib/*.jar包,都是WebClassLoader加載的。

Tomcat加載資源的概況圖:



當(dāng)Jsp文件修改的時(shí)候,Tomcat更新步驟:
1 但訪問1.jsp的時(shí)候,1.jsp的包裝類JspServletWrapper會去比較1.jsp文件最新修改時(shí)間和上次的修改時(shí)間,以此判斷1.jsp是否修改過。
2 1.jsp修改過的話,那么jspservletWrapper會清除相關(guān)引用,包括1.jsp編譯后的servlet實(shí)例和加載這個(gè)servlet的JasperLoader實(shí)例。
3 重新創(chuàng)建一個(gè)JasperLoader實(shí)例,重新加載修改過后的1.jsp,重新生成一個(gè)Servlet實(shí)例。
4 返回修改后的1.jsp內(nèi)容給用戶。
圖:Jsp清除引用和資源


當(dāng)app下面的class文件修改的時(shí)候,Tomcat更新步驟:
1 Context容器會有專門線程監(jiān)控app下面的類的修改情況。
2 如果發(fā)現(xiàn)有類被修改了。那么調(diào)用Context.reload()。清楚一系列相關(guān)的引用和資源。
3 然后創(chuàng)新創(chuàng)建一個(gè)WebClassLoader實(shí)例,重新加載app下面需要的class。
圖:Context清除引用和資源 

     在一個(gè)有一定規(guī)模的應(yīng)用中,如果文件修改多次,重啟多次的話,java.lang.OutOfMemoryErrorPermGen space這個(gè)錯(cuò)誤的的出現(xiàn)非常頻繁。主要就是因?yàn)槊看沃貑⒅匦录虞d大量的class,超過了PermGen space設(shè)置的大小。兩種情況可能導(dǎo)致PermGen space溢出。一、GC(Garbage Collection)在主程序運(yùn)行期對PermGen space沒有進(jìn)行清理(GC的不可控行),二、重啟之前WebClassLoader加載的class在別的地方還存在著引用。這里有篇很好的文章介紹了class內(nèi)存泄露-here


參考:
http://blog.csdn.net/runanli/article/details/2972361(關(guān)于Class類加載器 內(nèi)存泄漏問題的探討)
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Tomcat 源代碼分析之ClassLoader
在運(yùn)行期動態(tài)加載jar包/class目錄
Ken Wu's Blog ? java類加載器體系結(jié)構(gòu)(含hotswap原理)
Android熱更新實(shí)現(xiàn)原理
javaee加密部署,tomcat使用自己的classloader解密【正解】
JVM真香系列:輕松理解class文件到虛擬機(jī)(下)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服