Java 類加載機制 ClassLoader Class.forName 內存管理 垃圾回收GC

Java之類加載機制 html

類加載是Java程序運行的第一步,研究類的加載有助於瞭解JVM執行過程,並指導開發者採起更有效的措施配合程序執行。
研究類加載機制的第二個目的是讓程序能動態的控制類加載,好比熱部署等,提升程序的靈活性和適應性。
1、簡單過程
Java程序運行的場所是內存,當在命令行下執行:
java HelloWorld
命令的時候,JVM會將HelloWorld.class加載到內存中,並造成一個Class的對象HelloWorld.class。
其中的過程就是類加載過程:
一、尋找jre目錄,尋找jvm.dll,並初始化JVM;
二、產生一個Bootstrap Loader(啓動類加載器);
三、Bootstrap Loader自動加載Extended Loader(標準擴展類加載器),並將其父Loader設爲Bootstrap Loader。
四、Bootstrap Loader自動加載AppClass Loader(系統類加載器),並將其父Loader設爲Extended Loader。
五、最後由AppClass Loader加載HelloWorld類。
以上就是類加載的最通常的過程。
2、類加載器各自搜索的目錄

爲了弄清楚這個問題,首先還要看看System類的API doc文檔。 java

一、Bootstrap Loader(啓動類加載器):加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。
二、Extended Loader(標準擴展類加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運行程序時,也能夠指定其搜索路徑,例如:java -Djava.ext.dirs=d:/projects/testproj/classes HelloWorld
三、AppClass Loader(系統類加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運行程序時,也能夠加上-cp來覆蓋原有的Classpath設置,例如: java -cp ./lavasoft/classes HelloWorld
ExtClassLoader和AppClassLoader在JVM啓動後,會在JVM中保存一份,而且在程序運行中沒法改變其搜索路徑。若是想在運行時從其餘搜索路徑加載類,就要產生新的類加載器。
3、類加載器的特色
一、運行一個程序時,老是由AppClass Loader(系統類加載器)開始加載指定的類。
二、在加載類時,每一個類加載器會將加載任務上交給其父,若是其父找不到,再由本身去加載。
三、Bootstrap Loader(啓動類加載器)是最頂級的類加載器了,其父加載器爲null.
4、類加載器的獲取
很容易,看下面例子
public  class HelloWorld { 
         public  static  void main(String[] args) { 
                 HelloWorld hello =  new HelloWorld(); 
                 Class c = hello.getClass(); 
                 ClassLoader loader = c.getClassLoader(); 
                 System.out.println(loader); 
                 System.out.println(loader.getParent()); 
                 System.out.println(loader.getParent().getParent()); 
         } 
}
打印結果:
sun.misc.Launcher$AppClassLoader@19821f 
sun.misc.Launcher$ExtClassLoader@addbf1 
null 

Process finished with exit code 0
從上面的結果能夠看出,並無獲取到ExtClassLoader的父Loader,緣由是Bootstrap Loader(啓動類加載器)是用C語言實現的,找不到一個肯定的返回父Loader的方式,因而就返回null。
5、類的加載
類加載有三種方式:
一、命令行啓動應用時候由JVM初始化加載
二、經過Class.forName()方法動態加載
三、經過ClassLoader.loadClass()方法動態加載
三種方式區別比較大,看個例子就明白了:
public  class HelloWorld { 
         public  static  void main(String[] args)  throws ClassNotFoundException { 
                 ClassLoader loader = HelloWorld. class.getClassLoader(); 
                 System.out.println(loader); 
                 //使用ClassLoader.loadClass()來加載類,不會執行初始化塊 
                 loader.loadClass( "Test2"); 
                 //使用Class.forName()來加載類,默認會執行初始化塊 
//                 Class.forName("Test2"); 
                 //使用Class.forName()來加載類,並指定ClassLoader,初始化時不執行靜態塊 
//                 Class.forName("Test2", false, loader); 
         } 
}
public  class Test2 { 
         static { 
                 System.out.println( "靜態初始化塊執行了!"); 
         } 
}
分別切換加載方式,會有不一樣的輸出結果。
6、自定義ClassLoader
爲了說明問題,先看例子:
package test; 

import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 

/** 
* 自定義ClassLoader 

* @author leizhimin 2009-7-29 22:05:48 
*/
 
public  class MyClassLoader { 
         public  static  void main(String[] args)  throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { 
                 URL url =  new URL( "file:/E://projects//testScanner//out//production//testScanner"); 
                 ClassLoader myloader =  new URLClassLoader( new URL[]{url}); 
                 Class c = myloader.loadClass( "test.Test3"); 
                 System.out.println( "----------"); 
                 Test3 t3 = (Test3) c.newInstance(); 
         } 
}
public  class Test3 { 
         static { 
                 System.out.println( "Test3的靜態初始化塊執行了!"); 
         } 
}
運行後:
---------- 
Test3的靜態初始化塊執行了! 

Process finished with exit code 0
能夠看出自定義了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3加載到內存了,並經過默認構造方法構造了對象Test3 t3 = (Test3) c.newInstance();
有關ClassLoader還有很重要一點:
同一個ClassLoader加載的類文件,只有一個Class實例。可是,若是同一個類文件被不一樣的ClassLoader載入,則會有兩份不一樣的ClassLoader實例(前提是着兩個類加載器不能用相同的父類加載器)。

在java.lang包裏有個ClassLoader類,ClassLoader 的基本目標是對類的請求提供服務,按需動態裝載類和資
源,只有當一個類要使用(使用new 關鍵字來實例化一個類)的時候,類加載器纔會加載這個類並初始化。
一個Java應用程序可使用不一樣類型的類加載器。例如Web Application Server中,Servlet的加載使用開發
商自定義的類加載器, java.lang.String在使用JVM系統加載器,Bootstrap Class Loader,開發商定義的其餘類
則由AppClassLoader加載。在JVM裏由類名和類加載器區別不一樣的Java類型。所以,JVM容許咱們使用不一樣
的加載器加載相同namespace的java類,而實際上這些相同namespace的java類能夠是徹底不一樣的類。這種
機制能夠保證JDK自帶的java.lang.String是惟一的。
2. 加載類的兩種方式:
(1)  隱式方式
使用new關鍵字讓類加載器按需求載入所需的類
(2)  顯式方式
由 java.lang.Class的forName()方法加載
public static Class forName(String className)
public static Class forName(String className, boolean initialize,ClassLoader loader)
參數說明:
        className - 所需類的徹底限定名
        initialize - 是否必須初始化類(靜態代碼塊的初始化)
        loader - 用於加載類的類加載器 程序員

調用只有一個參數的forName()方法等效於 Class.forName(className, true, loader)。
這兩個方法,最後都要鏈接到原生方法forName0(),其定義以下:
private static native Class forName0(String name, boolean initialize,ClassLoader loader)
 throws ClassNotFoundException;
只有一個參數的forName()方法,最後調用的是:
forName0(className, true, ClassLoader.getCallerClassLoader());
而三個參數的forName(),最後調用的是:
forName0(name, initialize, loader);
因此,無論使用的是new 來實例化某個類、或是使用只有一個參數的Class.forName()方法,內部都隱含
了「載入類 + 運行靜態代碼塊」的步驟。而使用具備三個參數的Class.forName()方法時,若是第二個參數
爲false,那麼類加載器只會加載類,而不會初始化靜態代碼塊,只有當實例化這個類的時候,靜態代碼塊
纔會被初始化,靜態代碼塊是在類第一次實例化的時候才初始化的。
直接使用類加載器
得到對象所屬的類 : getClass()方法
得到該類的類加載器 : getClassLoader()方法
3.執行java XXX.class的過程
找到JRE——》找到jvm.dll——》啓動JVM並進行初始化——》產生Bootstrap Loader——》
載入ExtClassLoader——》載入AppClassLoader——》執行java XXX.class web




ClassLoader是用來處理類加載的類,它管理着具體類的運行時上下文。 算法

1.ClassLoader存在的模塊意義: bootstrap

1)從java的package定義出發: api

   classloader是經過分層的關聯方式來管理運行中使用的類,不一樣的classloader中管理的類是不相同的,或者即使兩個類毫無二致(除了路徑)也是不一樣的兩個類,在進行強制轉換時也會拋出ClassCastException。因此,經過classloader的限制,咱們能夠創建不一樣的package路徑以區別不一樣的類(注意這裏的「不一樣」是指,命名和實現徹底一致,可是有不一樣的包路徑。)。那麼也是由於有特定的classloader,咱們能夠實現具體模塊的加載,而不影響jvm中其餘類,即發生類加載的衝突。 緩存

2)可是,若是兩個在不一樣路徑下的類(咱們假定,這兩個類定義中,不存在package聲明,徹底同樣的兩個類),通過不一樣的classloader加載,這兩個類在jvm中產生的實例能夠相互轉換嗎? tomcat

答案是否認的。即使這兩個類除了存在位置不一樣以外,都徹底同樣。經由不一樣classloader加載的兩個類依然是不一樣的兩個對象。經過Class.newInstance()或者Class.getConstructor().newInstance()產生的對象是徹底不一樣的實例。 安全

以上兩種狀況,package可使得咱們的軟件架構清晰,但那不是最終做用,若是跟classloader結合起來理解,效果更好。

2.ClassLoader的類加載機制:

   ClassLoader做爲java的一個默認抽象類,給咱們帶來了極大的方便,若是咱們要本身實現相應的類加載算法的話。

   每一個類都有一個對應的class與之綁定,而且能夠經過MyClass.class方式來獲取這個Class對象。經過Class對象,咱們就能獲取加載這個類的classloader。可是,咱們如今要研究的是,一個類,是如何經過classloader加載到jvm中的。

   其中有幾個關鍵方法,值得咱們瞭解一番:

   protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException;

咱們能夠假設一個實例在創建時,例如經過new方式,是經由如此步驟實現:ClassLoader.loadClass("classname",false).newInstance()。

接下來須要考慮的是loadClass方法爲咱們作了哪些工做?如何跟對應的.class文件結合,如何將對應的文件變成咱們的Class對象,如何得到咱們須要的類?

在ClassLoader類中,已經有了loadClass默認實現。咱們結合源代碼說明一下:

protected synchronized Class<?> loadClass(String name, boolean resolve)

throws ClassNotFoundException

    {

// 首先檢查,jvm中是否已經加載了對應名稱的類,findLoadedClass(String )方法其實是findLoadedClass0方法的wrapped方法,作了檢查類名的工

       //做,而findLoadedClass0則是一個native方法,經過底層來查看jvm中的對象。

Class c = findLoadedClass(name);

if (c == null) {//類還未加載

    try {

if (parent != null) {

                    //在類還未加載的狀況下,咱們首先應該將加載工做交由父classloader來處理。

    c = parent.loadClass(name, false);

} else {

                    //返回一個由bootstrap class loader加載的類,若是不存在就返回null

    c = findBootstrapClassOrNull(name);

}

    } catch (ClassNotFoundException e) {

                // ClassNotFoundException thrown if class not found

                // from the non-null parent class loader

            }

            if (c == null) {

        // If still not found, then invoke findClass in order

        // to find the class.

        c = findClass(name);//這裏是咱們的入手點,也就是指定咱們本身的類加載實現

    }

}

if (resolve) {

    resolveClass(c);//用來作類連接操做

}

return c;

    }

 

在這段代碼中,應該已經說明了不少問題,那就是jvm會緩存加載的類,因此,在咱們要求classloader爲咱們加載類時,要先經過findLoadedClass方法來查看是否已經存在了這個類。不存在時,就要先由其parent class loader 來loadClass,固然能夠迭代這種操做一直到找到這個類的加載定義。若是這樣仍是不能解決問題,對於咱們本身實現的class loader而言,能夠再交由system class loader來loadClass,若是再不行,那就讓findBootstrapClassOrNull。經歷瞭如此路程,依然不能解決問題時,那就要咱們出馬來擺平,經過本身實現的findClass(String)方法來實現具體的類加載。

這段實現代碼摘自Andreas Schaefer寫的文章中的代碼(這篇文章至關精彩)

protected Class findClass( String pClassName )

           throws ClassNotFoundException {

       try {

           System.out.println( "Current dir: " + new File( mDirectory ).getAbsolutePath() );

           File lClassFile = new File( mDirectory, pClassName + ".class" );

           InputStream lInput = new BufferedInputStream( new FileInputStream( lClassFile ) );

           ByteArrayOutputStream lOutput = new ByteArrayOutputStream();

           int i = 0;

           while( ( i = lInput.read() ) >= 0 ) {

               lOutput.write( i );

           }

           byte[] lBytes = lOutput.toByteArray();

           return defineClass( pClassName, lBytes, 0, lBytes.length );

       } catch( Exception e ) {

           throw new ClassNotFoundException( "Class: " + pClassName + " could not be found" );

       }

   }

findClass方法主要的工做是在指定路徑中查找咱們須要的類。若是存在此命名的類,那麼就將class文件加載到jvm中,再由defineClass方法(一個native方法)來生成具體的Class對象。

通常來講,通過上述方式來加載類的話,咱們的類可能都在一個classloader中加載完成。可是,再強調一下,那就是若是類有不一樣路徑或者不一樣包名,那就是不一樣類定義。

java classLoader 體系結構


  1. Bootstrap ClassLoader/啓動類加載器 
    主要負責jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工做。
  2. Extension ClassLoader/擴展類加載器 
    主要負責jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工做。
  3. System ClassLoader/系統類加載器 
    主要負責java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工做。
  4. User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類) 
    在程序運行期間, 經過java.lang.ClassLoader的子類動態加載class文件, 體現java動態實時類裝入特性。

類加載器的特性:

  1. 每一個ClassLoader都維護了一份本身的名稱空間, 同一個名稱空間裏不能出現兩個同名的類。
  2. 爲了實現java安全沙箱模型頂層的類加載器安全機制, java默認採用了 " 雙親委派的加載鏈 " 結構。
classloader-architecture

classloader-architecture

classloader-class-diagram

classloader-class-diagram

類圖中, BootstrapClassLoader是一個單獨的java類, 其實在這裏, 不該該叫他是一個java類。由於,它已經徹底不用java實現了。它是在jvm啓動時, 就被構造起來的, 負責java平臺核心庫。

自定義類加載器加載一個類的步驟

classloader-load-class

classloader-load-class

ClassLoader 類加載邏輯分析, 如下邏輯是除 BootstrapClassLoader 外的類加載器加載流程:

  1. // 檢查類是否已被裝載過  
  2. Class c = findLoadedClass(name);  
  3. if (c == null ) {  
  4.      // 指定類未被裝載過  
  5.      try {  
  6.          if (parent != null ) {  
  7.              // 若是父類加載器不爲空, 則委派給父類加載  
  8.              c = parent.loadClass(name, false );  
  9.          } else {  
  10.              // 若是父類加載器爲空, 則委派給啓動類加載加載  
  11.              c = findBootstrapClass0(name);  
  12.          }  
  13.      } catch (ClassNotFoundException e) {  
  14.          // 啓動類加載器或父類加載器拋出異常後, 當前類加載器將其  
  15.          // 捕獲, 並經過findClass方法, 由自身加載  
  16.          c = findClass(name);  
  17.      }  
  18. }  

線程上下文類加載器
java默認的線程上下文類加載器是 系統類加載器(AppClassLoader)。

  1. // Now create the class loader to use to launch the application  
  2. try {  
  3.     loader = AppClassLoader.getAppClassLoader(extcl);  
  4. catch (IOException e) {  
  5.     throw new InternalError(  
  6. "Could not create application class loader" );  
  7. }   
  8.   
  9. // Also set the context class loader for the primordial thread.  
  10. Thread.currentThread().setContextClassLoader(loader);  

以上代碼摘自sun.misc.Launch的無參構造函數Launch()。

使用線程上下文類加載器, 能夠在執行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文裏的類加載器加載類.
典型的例子有, 經過線程上下文來加載第三方庫jndi實現, 而不依賴於雙親委派.
大部分java app服務器(jboss, tomcat..)也是採用contextClassLoader來處理web服務。
還有一些採用 hotswap 特性的框架, 也使用了線程上下文類加載器, 好比 seasar (full stack framework in japenese).

線程上下文從根本解決了通常應用不能違背雙親委派模式的問題.
使java類加載體系顯得更靈活.

隨着多核時代的來臨, 相信多線程開發將會愈來愈多地進入程序員的實際編碼過程當中. 所以,
在編寫基礎設施時, 經過使用線程上下文來加載類, 應該是一個很好的選擇。

固然, 好東西都有利弊. 使用線程上下文加載類, 也要注意, 保證多根鬚要通訊的線程間的類加載器應該是同一個,
防止由於不一樣的類加載器, 致使類型轉換異常(ClassCastException)。

爲何要使用這種雙親委託模式呢?

  1. 由於這樣能夠避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。
  2. 考慮到安全因素,咱們試想一下,若是不使用這種委託模式,那咱們就能夠隨時使用自定義的String來動態替代java核心api中定義類型,這樣會存在很是大的安全隱患,而雙親委託的方式,就能夠避免這種狀況,由於String已經在啓動時被加載,因此用戶自定義類是沒法加載一個自定義的ClassLoader。

java動態載入class的兩種方式:

  1. implicit隱式,即利用實例化才載入的特性來動態載入class
  2. explicit顯式方式,又分兩種方式:
    1. java.lang.Class的forName()方法
    2. java.lang.ClassLoader的loadClass()方法

用Class.forName加載類

Class.forName使用的是被調用者的類加載器來加載類的。
這種特性, 證實了java類加載器中的名稱空間是惟一的, 不會相互干擾。
即在通常狀況下, 保證同一個類中所關聯的其餘類都是由當前類的類加載器所加載的。

  1. public static Class forName(String className)  
  2.      throws ClassNotFoundException {  
  3.      return forName0(className, true , ClassLoader.getCallerClassLoader());  
  4. }   
  5.   
  6. /** Called after security checks have been made. */  
  7. private static native Class forName0(String name, boolean initialize,  
  8. ClassLoader loader)  
  9.      throws ClassNotFoundException;  

上面中 ClassLoader.getCallerClassLoader 就是獲得調用當前forName方法的類的類加載器

static塊在何時執行?

  • 當調用forName(String)載入class時執行,若是調用ClassLoader.loadClass並不會執行.forName(String,false,ClassLoader)時也不會執行.
  • 若是載入Class時沒有執行static塊則在第一次實例化時執行.好比new ,Class.newInstance()操做
  • static塊僅執行一次

各個java類由哪些classLoader加載?

  • java類能夠經過實例.getClass.getClassLoader()得知
  • 接口由AppClassLoader(System ClassLoader,能夠由ClassLoader.getSystemClassLoader()得到實例)載入
  • ClassLoader類由bootstrap loader載入

NoClassDefFoundError和ClassNotFoundException

  • NoClassDefFoundError:當java源文件已編譯成.class文件,可是ClassLoader在運行期間在其搜尋路徑load某個類時,沒有找到.class文件則報這個錯
  • ClassNotFoundException:試圖經過一個String變量來建立一個Class類時不成功則拋出這個異常


垃圾回收分爲兩大步驟:識別垃圾 和回收垃圾

    識別垃圾有兩大基本方法

   1.計數器法

每一個對象有一個相應的計數器,統計當前被引用的個數,每次被引用或者失去引用都會更新該計數器。

優勢:識別垃圾快,只需判斷計數器是否爲零。

缺點:增長了維護計數器的成本,沒法在對象互相引用的狀況下識別垃圾,所以,適用於對實時性要求很是高的系統。

   2.追蹤法

從根對象(例如局部變量)出發,逐一遍歷它的引用。若沒法被掃描到,即認定爲垃圾,實際狀況中通常採用該方法。

    回收垃圾最重要的是要最大限度地減小內存碎片。

   兩種兩大基本方法:

   1.移動活對象覆蓋內存碎片,使對象間的內存空白增大。

   2.拷貝全部的活對象到另一塊完整的空白內存,而後一次釋放原來的內存。

   一般第二種方法可以最大的減小內存碎片,可是缺點是在拷貝過程當中會終止程序的運行。

引入分級的概念,一般一個程序中大部分對象的生命週期很短,只有小部分的對象有比較長的生命。而偏偏使得拷貝方法性能打折扣的是重複拷貝那些長命的對象。所以,把對象分紅幾個級別,在低級別呆到必定時間就將其升級。相應地越高級別,回收的次數越少。最理想的狀況是,每次回收最低級別的對象所有失效,一次性就能夠回收該級別全部內存,提升效率。同時,因爲每次只回收一個級別,不需遍歷全部對象,控制了整個回收的時間。

   因爲垃圾識別是經過識別引用來達到,爲了增長程序對垃圾回收的控制。提供了引用對象的概念,細化了引用的類型,分別是StrongReferenceSoftReference, WeakReference, PhantomReference。其中強引用就是普通的java引用,其餘三種類型至關於一個包裝器,一方面使得垃圾回收器區分引用類型作不一樣的處理,另外一方面程序經過他們仍然能夠獲得強引用。

       分代垃圾回收機制:

 

   如上圖所示,現代GC採用分區管理機制的JVMJVM所管理的全部內存資源分爲2個大的部分。永久存儲區(Permanent Space)和堆空間(The Heap Space)。其中堆空間又分爲新生區(Young (New) generation space)和養老區(Tenure (Old) generation space),新生區又分爲伊甸園(Eden space),倖存者0區(Survivor 0 space)和倖存者1區(Survivor 1 space)。具體分區以下圖:

   JVM他的這些分區各有什麼用途,請看下面的解說。

  永久存儲區(Permanent Space):永久存儲區是JVM的駐留內存,用於存放JDK自身所攜帶的Class,Interface的元數據,應用服務器容許必須的Class,Interface的元數據和Java程序運行時須要的ClassInterface的元數據。被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉JVM時,釋放此區域所控制的內存。 

   堆空間(The Heap Space):是JAVA對象生死存亡的地區,JAVA對象的出生,成長,死亡都在這個區域完成。堆空間又分別按JAVA對象的建立和年齡特徵分爲養老區和新生區。

   新生區(Young (New) generation space ):新生區的做用包括JAVA對象的建立和從JAVA對象中篩選出能進入養老區的JAVA對象。

   伊甸園(Eden space):JAVA對空間中的全部對象在此出生,該區的名字所以而得名。也便是說當你的JAVA程序運行時,須要建立新的對象,JVM將在該區爲你建立一個指定的對象供程序使用。建立對象的依據便是永久存儲區中的元數據。

   倖存者0區(Survivor 0 space)和倖存者1區(Survivor1 space):當伊甸園的控件用完時,程序又須要建立對象;此時JVM的垃圾回收器將對伊甸園區進行垃圾回收,將伊甸園區中的再也不被其餘對象所引用的對象進行銷燬工做。同時將伊甸園中的還有其餘對象引用的對象移動到倖存者0區。倖存者0區就是用於存放伊甸園垃圾回收時所幸存下來的JAVA對象。當將伊甸園中的還有其餘對象引用的對象移動到倖存者0區時,若是倖存者0區也沒有空間來存放這些對象時,JVM的垃圾回收器將對倖存者0區進行垃圾回收處理,將倖存者0區中不在有其餘對象引用的JAVA對象進行銷燬,將倖存者0區中還有其餘對象引用的對象移動到倖存者1區。倖存者1區的做用就是用於存放倖存者0區垃圾回收處理所幸存下來的JAVA對象。

   養老區(Tenure (Old) generation space):用於保存重新生區篩選出來的JAVA對象。

上面咱們看了JVM的內存分區管理,如今咱們來看JVM的垃圾回收工做是怎樣運做的。首先當啓動J2EE應用服務器時,JVM隨之啓動,並將JDK的類和接口,應用服務器運行時須要的類和接口以及J2EE應用的類和接口定義文件也及編譯後的Class文件或JAR包中的Class文件裝載到JVM的永久存儲區。在伊甸園中建立JVM,應用服務器運行時必須的JAVA對象,建立J2EE應用啓動時必須建立的JAVA對象;J2EE應用啓動完畢,可對外提供服務。

JVM在伊甸園區根據用戶的每次請求建立相應的JAVA對象,當伊甸園的空間不足以用來建立新JAVA對象的時候,JVM的垃圾回收器執行對伊甸園區的垃圾回收工做,銷燬那些再也不被其餘對象引用的JAVA對象(若是該對象僅僅被一個沒有其餘對象引用的對象引用的話,此對象也被歸爲沒有存在的必要,依此類推),並將那些被其餘對象所引用的JAVA對象移動到倖存者0區。

   若是倖存者0區有足夠控件存放則直接放到倖存者0區;若是倖存者0區沒有足夠空間存放,則JVM的垃圾回收器執行對倖存者0區的垃圾回收工做,銷燬那些再也不被其餘對象引用的JAVA對象(若是該對象僅僅被一個沒有其餘對象引用的對象引用的話,此對象也被歸爲沒有存在的必要,依此類推),並將那些被其餘對象所引用的JAVA對象移動到倖存者1區。

若是倖存者1區有足夠控件存放則直接放到倖存者1區;若是倖存者0區沒有足夠空間存放,則JVM的垃圾回收器執行對倖存者0區的垃圾回收工做,銷燬那些再也不被其餘對象引用的JAVA對象(若是該對象僅僅被一個沒有其餘對象引用的對象引用的話,此對象也被歸爲沒有存在的必要,依此類推),並將那些被其餘對象所引用的JAVA對象移動到養老區。

   若是養老區有足夠控件存放則直接放到養老區;若是養老區沒有足夠空間存放,則JVM的垃圾回收器執行對養老區區的垃圾回收工做,銷燬那些再也不被其餘對象引用的JAVA對象(若是該對象僅僅被一個沒有其餘對象引用的對象引用的話,此對象也被歸爲沒有存在的必要,依此類推),並保留那些被其餘對象所引用的JAVA對象。若是到最後養老區,倖存者1區,倖存者0區和伊甸園區都沒有空間的話,則JVM會報告「JVM堆空間溢出(java.lang.OutOfMemoryError: Java heap space)」,也便是在堆空間沒有空間來建立對象。

   這就是JVM的內存分區管理,相比不分區來講;通常狀況下,垃圾回收的速度要快不少;由於在沒有必要的時候不用掃描整片內存而節省了大量時間。

相關文章
相關標籤/搜索