JVM是JAVA平臺的重要組成之一,因涉及知識點太多,故從如下幾個方面對JVM進行淺層面的介紹,若是須要深刻理解,推薦學習機械工業出版社的《深刻理解JAVA虛擬機》。html
請尊重做者勞動成果,轉載請標明原文連接:java
http://www.javashuo.com/article/p-rvguseyp-cd.html算法
1、JAVA內存結構bootstrap
Java虛擬機規範中規定的JVM運行時數據區以下圖所示:數組
整體來講,分爲線程共享部分(方法區、堆)和線程隔離區(虛擬機棧、本地方法棧和程序計數器)。瀏覽器
1.方法區安全
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。其中常量存儲於運行時常量區中,運行時常量區是區的一部分,用於存儲編譯期生成的字面量和符號引用。但運行時常量區的內容並不僅是在編譯期間產生,經過String.intern()也能夠實如今運行時向常量區中添加內容。服務器
2.堆數據結構
是JVM中最大的一塊內存區域,該區域的目的只是用於存儲對象實例及數組。該區域也是GC的最主要區域。多線程
3.虛擬機棧
每一個線程方法在執行時都會建立一個棧幀,包含局部變量表、返回地址、操做數棧等信息。每一個方法的執行與完成就對應的棧幀的入棧與出棧過程 。局部變量表佔用空間的大小在編譯期就肯定了。
4.本地方法棧
與虛擬機棧相似,不過其中執行是本地方法。對於HotSpot虛擬機而言,本地方法棧和虛擬機棧是統一的。
5.程序計數器
是一個小的內存空間,若是線程正在執行的是一個java方法,則此內存區域記錄正在執行的虛擬機字節碼指令;若是線程正在執行的是native方法,則計算器中的值爲空。
2、JAVA垃圾回收機制
JAVA的垃圾回收主要涉及到肯定對象是否存活、垃圾收集等算法,其中肯定對象回收算法採用的是可達性分析算法,垃圾收集目前各JVM廠商普遍採用的是分代收集算法。這裏面主要描述下分代收集算法的過程。
分代收集算法的核心思想是將內存區域按照對象的生存週期階段進行劃分,其中將堆區劃分爲新生代(young generation)和老年代(old generation)。將非堆區(通常指方法區)劃分爲持久代(permanent generation)。
1.新生代
新生代又可再分爲Eden區和兩個Survivor區(兩個Survivor區的大小是同樣的,便於交換)。新生成的對象都會先在新生代的Eden區進行保存。新生代的特色是每次垃圾回收都會有大量的內存被回收,並且收集比較頻繁,因此新生代適合以下的收集算法:
首先,新生成的對象分配到Eden區,若是eden區滿了,則將可達性的對象複製到survivor1區,後清空eden區。
而後,若是survivor1區滿了,則將eden區與survivor1區的可達性對象複製到survivor2區,後清空eden區和survivor1區,清空完後將survivor2區與survivor1區交換,即保持survivor2是空的。
再次,若是survivor2區也滿了,則將eden區、survivor1區、survivor2區的可達性對象複製到老年代中,並清空新生代中。
最後,若是老年代也滿了,就觸發full gc了。
2.老年代
老年代的內存比新生代大的多,這個區域執行垃圾回收的頻度不高。當老年代滿時,會觸發full gc。
3.持久代
持久代通常指方法區,該區須要回收的有廢棄的常量和類。對於常量可用可達性分析的方法進行判斷回收,對於類則須要同時知足如下條件纔會被回收:
首先,該類的全部實例對象都已被回收;
其次,該類的類加載器也已被回收;
再次,該類的Class方法沒有在任何地方被引用,即沒法經過在任何地方經過反射訪問到該類的方法。
4.何時會解決垃圾回收?
綜上所述,當eden滿時,就會觸發scavenge gc,當出現如下狀況時會觸發full gc:
老年代已滿;
持久代已滿;
調用System.gc()方法;
3、JAVA類加載過程
JVM類加載過程具體裝載、驗證、準備、解析、初始化這五個部分。
1.裝載
在裝載過程當中,須要完成如下事情:
1)經過類的全限定名獲取類的二進制字節流;
2)將類的二進制字節流轉換爲方法區的運行時數據結構;
3)生成一個表明此類的java.lang.Class對象,做爲方法區這個類的各類數據的訪問入口。
2.驗證
驗證、解析和初始化又稱爲是鏈接階段,在驗證驗證主要是確保二進制字節流符合JVM的規範,不會危害計算機的安全。具體驗證階段須要作的事情以下:
1)文件格式驗證,驗證字節流是否符合Class文件格式規範;
2)元數據驗證,對字節碼進行語義驗證,以保證其描述信息符合JAVA語言規範;
3)字節碼驗證,經過數據流和控制流分析,肯定程序語義是合法的、符合邏輯的;
4)符號引用驗證,對常量池中的各類符號引用的信息進行匹配性驗證。
3.準備
準備的過程實際上是分配內存的過程。在這個階段有兩個容易產生混淆的概念:一是此階段分配內存的只是類變量(static變量),不包含實例變量,實例變量的內存分配是在對象實例化時隨對象一塊兒分配在堆中;二是該階段分配內存中保存的值只是數據類型的零值,具體值須要在初始化階段進行賦值。也有特殊狀況,就是對於靜態常量(final修飾)會在準備階段將值賦值爲真實值。
4.解析
解析階段就是將常量池內折符號引用轉換爲直接引用的過程,具體包括類和接口的解析、字段的解析、方法的解析、接口方法和解析。
5.初始化
初始化階段其實就是執行類構造函數(clinit)的階段。對於clinit()須要說明如下幾點:
1)clinit()中的程序是自動收集類中static變量及static塊產生的,執行順序與代碼中的順序一致。靜態語句塊中只能訪問在其以前聲明的static變量,在其以後聲明的static變量只能賦值,不能訪問。
2)執行clinit()方法前,JVM會自動調用父類的clinit()方法;
3)虛擬機會保證一個類的clinit()在多線程環境中,自動加鎖、同步。
4、JVM的類加載器
JVM的類加載是經過類加載器實現的,經常使用的類加載器包括下面三種:
1.啓動類加載器(bootstrap classloader):加載{JDK_HOME}/lib下的類
2.擴展類加載器(extension classloader):加載{JDK_HOME}/lib/ext下的類
3.應用程序類加載器(application classloader):加載classpath指定的類
對於不一樣類加載器以及他們之間的協做能夠參考下面的雙親委派模型。
雙親委派模型的工做過程是:若是一個類加載器收到了類的加載請求,會首先把請求委派給本身的父類,每一個層次的類加載器都會如此,由於全部的加載請求最終都會發送到bootstarp加載器中,只有當父加載器確實沒法本身完成加載請求時,子加載器纔會嘗試本身加載。
雙親委派模型使得JAVA類可以按層次進行加載,不會形成混亂。
5、JVM的相關工具
JDK中有不少強大的監控工具,能夠直接在命令行運行。這對於在生產環境進行監控是很是有用的。例如SUN JDK中就包含了如下監控和故障處理工具。
jps: jvm process status tool,顯示指定系統內全部的hotspot虛擬機進程
jstat: jvm statistics monitoring tool,用於收集hotspot虛擬機各方面的運行數據
jinfo: configuration info for java,顯示虛擬機配置信息
jmap: memory map for java,生成虛擬機的內存轉儲快照(heapdump文件)
jhat: jvm heap dump browser,用於分析heapmap文件,它會創建一個http/html服務器,讓用戶能夠在瀏覽器上查看分析結果
jstack: stack trace for java ,顯示虛擬機的線程快照
先介紹這麼多,後面有機會再介紹JVM在併發方面的相關支持。