1. 程式人生 > >Java平臺與垃圾回收機制

Java平臺與垃圾回收機制

  1. JDK與JRE的區別?
    ①JVM(Java Virtual Machine,Java虛擬機器)是實現java跨平臺的核心,負責解釋執行class檔案.
    ②JRE(Java Runntime Environment, java執行環境)是執行Java程式所必須的環境的集合,不包括JVM標準實現以及Java核心類庫. JRE = JVM + lib(所需類庫)
    ③JDK(Java Development Kit,Java開發工具包) 是整個Java的核心,包括 JRE,許多開發與除錯工具和Java基礎的類庫(即Java API,包括rt.jar). JDK = JRE+ Java開發工具

  2. Java類載入器的原理及其組織結構
    ①Java語言是一種具有動態性的解釋性語言,類只有被載入到JVM中後才能執行.當執行程式時,JVM會將編譯生成的.class檔案按照需求和一定的規則載入到記憶體中,並組織稱為一個完整的Java應用程式.這個載入過程是由載入器來完成的,具體而言,就是有ClassLoader和他的子類來實現的.類載入器本身也是一個類,其實質是把類檔案從硬碟讀取到記憶體中.
    ②類的載入方式分為隱式裝載和顯示裝載兩種.
    a. 隱式裝載:指的是程式在使用new等方式建立物件的時候,會隱式地呼叫類的載入器把對應的類載入到JVM中.
    b. 顯示裝載:是值直接呼叫class.forName()方法來吧所需的類載入到JVM中.
    ③ 在Java語言中,類的載入是動態的.它不會一次性的將所有類全部載入後再執行,而是抱著程式執行的基類完全載入到JVM中,至於其他類,則在需要的時候才載入.
    ④在Java語言中,可以把類分為三類:系統類,擴充套件類和自定義類.
    Bootstrap Loader --> 負責載入系統類(jre/lib/rt .jar的類)
    |
    –ExtClassLoader -->負責載入擴充套件類(jar/lib/ext/* .jar的類)
    |
    – AppClassLoader -->負責載入應用類(classpath指定的目錄或jar中的類)
    以上三個類,通過委託的方式來完成類的載入,具體來說,就是當有類需要被載入時,類載入器會請求父類來完成這個載入工作,父類會使用自己的搜尋路徑來搜尋需要被載入的類,如果搜尋不到,才會由子類按照其搜尋路徑來搜尋待載入的類.

  3. JVM的工作原理是什麼?

	為了便於管理,JVM在執行Java程式的時候,會把他所管理的記憶體劃分為多個不同的區域.
	①class檔案
	class檔案時Java程式編譯後產生的中間程式碼,這些中間程式碼將會被JVM解釋執行.
	②類裝載器子系統
	JVM有兩種類裝載器,分別是啟動類裝載器和使用者自定義類裝載器.其中,啟動類裝載器是JVM實現的一部分;使用者自定義類裝載器則是Java程式的一部分,必須是ClassLoader的子類,常用的類載入器主要有以下幾種:
		a. BootStrap ClassLoader.這是JVM的根ClassLoader,它是用c++語言實現的,當JVM啟動時,初始化此ClassLoader,並由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(SUN JDK的實現)中所有class檔案的載入,這個jar中包含了Java規範定義的所有介面以及實現.
		b. Extension ClassLoader. JVM用此ClassLoader來載入擴充套件功能的一些jar包.
		c. System ClassLoader. JVM用此ClassLoader來載入啟動引數中指定的Classpath中的jar包以及目錄,在Sun JDK中,ClassLoader對應的類名為AppClassLoader
		d. User-Defined ClassLoader. User-Defined ClassLoader是Java開發人員繼承ClassLoader抽象類自行實現的ClassLoader,基於自定義的ClassLoader可用於載入非Classpath中的jar以及目錄.
	③ 方法區
	方法區用來儲存被虛擬機器載入的類資訊,常量,靜態變數,和編譯器編譯後的程式碼(.class檔案)等資料.在類載入class檔案時,這些資訊將會被提取出來,並存儲到方法區中.這個區域是所有執行緒共享的區域,所以它被設計為執行緒安全的.
	方法區中還存放了執行時的常量池,例如字串常量池.
	④堆
	堆是虛擬機器啟動的時候建立的被所有執行緒共享的區域.這塊區域主要用來存放物件的例項,通過new操作創建出來物件的例項都儲存在堆空間中. 因此堆是垃圾回收器管理的重點區域.
	⑤虛擬機器棧
	棧是執行緒私有的區域 , 每當有新的執行緒建立時,就會給它分配一個棧空間,當執行緒結束後,棧空間就會被回收.因此,棧與執行緒擁有相同的宣告週期.
	棧主要用來實現Java語言中方法的呼叫和執行,每個方法在被執行的時候,都會建立一個棧幀用來儲存這個方法的區域性變數 , 操作棧 , 動態連結和方法出口等資訊.當進行方法呼叫時,通過壓棧與彈棧操作進行棧空間的分配與釋放.當一個方法被呼叫的時候,會壓入一個新的棧幀到這個執行緒的棧中,當方法呼叫結束後,就會彈出這個棧幀,從而回收呼叫這個方法使用的棧空間.
	⑥程式計數器
	程式計數器也是執行緒私有的資源,JVM會給每個執行緒建立單獨的程式計數器.它可以被看做是當前執行緒執行的位元組碼的行號指示器,直譯器的工作原理就是通過改變這個計數器的值來確定下一條需要被執行的位元組碼指令,程式控制的流程(迴圈,分支,異常處理,執行緒恢復)都是通過這個技術器來完成的.
	(7)本地方法棧
	與虛擬機器棧作用類似,只不過本地方法棧是為虛擬機器使用的Native(本地)方法服務
	(8)執行引擎
	執行引擎主要負責執行位元組碼.
	(9)垃圾回收器
	回收程式中不再使用的記憶體.
  1. 垃圾回收機制的原理是什麼?
    Java語言提供了垃圾回收器來自動檢測物件的作用域,實現自動地把不再被使用的儲存空間釋放掉.具體而言,垃圾回收機制主要負責完成3項任務:分配記憶體,確保被引用物件的記憶體不被錯誤地回收,回收不再被引用的物件的記憶體空間.
    垃圾回收器使用有向圖來記錄和管理堆記憶體中的所有物件.通過這個有向圖就可以識別哪些物件是可達的,哪些物件是不可達的,所有不可達物件都是可被垃圾回收器回收的.
    Java開發人員可以通過呼叫System.gc()方法來通知垃圾回收器執行,當然,JVM也並不會保證垃圾回收器馬上就會執行.由於gc()方法的執行會停止所有的響應,去檢查記憶體中是否有可回收的物件,這會對程式的正常執行以及效能造成極大的威脅,所以,實際程式設計中,不推薦頻繁使用gc方法.

  2. 如何使JVM的堆, 棧 和持久帶發生記憶體溢位?
    ①不斷的用new例項化物件並且一直保持對這些物件的引用,例項化足夠多的例項就會導致堆溢位
    如:
    List list = new ArrayList();
    while(true)
    l.add(new Object());

    ② 棧溢位 – 無限遞迴呼叫
    ③ 持久代.
    當一個類第一次被訪問的時候,JVM需要把類載入進來,而類載入器就會佔用持久代的空間來儲存classes資訊,持久帶中的資訊主要包括:類方法,類名,常量池,和JVM使用的內部物件等.當JVM需要載入一個新類的時候,如果持久代中沒有足夠的空間,此時就會丟擲Java.Lang.OutOfMemoryError:PermGen Space異常.
    所以,當代碼載入足夠多類的時候就會導致持久代溢位.(並不是所有的Java虛擬機器都有持久代的概念)

  3. Java堆被劃分為新生代和老生代,有什麼區別?
    根據物件的生命週期的長短把物件分成不同的種類:新生代,老生代和持久代. 進行分代垃圾回收.

     分代垃圾回收演算法的主要思路:把堆分為兩個或者多個子堆,每一個子堆被視為一代.在執行的過程中,優先收集哪些年幼的物件,如果一個物件經過多次收集仍然存活,那麼就可以把這個物件移到高一級的堆裡,減少對其掃描的次數.
     
     新生代 : 包括3部分. 1個Eden區和2個Survivor區. 
     	Eden區主要用來儲存新建的物件,
     	Survivor區是大小相等的兩塊區域,在使用"複製"回收演算法是,作為雙快取,起記憶體整理的作用.Survivor區始終保持一個是空的.
     老生代:儲存生命週期較長的物件,超大的物件(無法在新生代分配的物件)
     持久代:存放程式碼,字串常量池和靜態變數等.SunJDK把方法區實現在了永久代.
    

    minorGC : 主要用來對新生代進行垃圾回收,把Eden中不能被回收的物件放到空的Survivor區,另一個Survivor區不能被垃圾回收器回收的物件也會放到這個Survivor區中,這樣能保證有一個Survivor區是空的 . 如果在這個過程中,Survivor區也滿了,或者有些物件已經存在非常長的實際,這些物件就會被放到老生代中. 如果老生代也滿了,就會觸發fullGC
    fullGC: 用來清理整個堆空間,包括新生代和老生代.fullGC()會造成很大的記憶體開銷

    如何避免fullGC()?
    ①避免呼叫System.gc()方法,因為它會觸發fullGC
    ②老生代空間不足時會觸發fullGC,所以應儘量做到讓物件在MinorGC階段被回收,不要建立過大的物件及陣列.
    或者根據實際情況增大Survivor區,老生代空間或調低觸發併發GC的概率.
    ③ 永久代滿.可以增大永久代的空間(MaxPermSize=16m;)
    注意:Java8中移除了永久代,新加了一個稱為元資料的native記憶體區,所以大部分的類的元資料都在本地記憶體中分配.