原文:http://www.blogjava.net/heavensay/archive/2012/11/07/389685.html
這篇文章主要是分析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文件。下面看一個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 name102 * @param resolve103 * @return104 * @throws ClassNotFoundException105 */106 public Class customLoad(String name, boolean resolve,ClassLoader cl)107 throws ClassNotFoundException {108 //findClass(- 該類所有的實例都已經被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(新聞熱點
疑難解答