類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,而後在堆區建立一個 java.lang.Class對象,用來封裝類在方法區內的數據結構。類的加載的最終產品是位於堆區中的 Class對象, Class對象封裝了類在方法區內的數據結構,而且向Java程序員提供了訪問方法區內的數據結構的接口。java
類加載器並不須要等到某個類被「首次主動使用」時再加載它,JVM規範容許類加載器在預料某個類將要被使用時就預先加載它,若是在預先加載的過程當中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError錯誤)若是這個類一直沒有被程序主動使用,那麼類加載器就不會報告錯誤。程序員
類加載的過程包括了加載、驗證、準備、解析、初始化五個階段。在這五個階段中,加載、驗證、準備和初始化這四個階段發生的順序是肯定的,而解析階段則不必定,它在某些狀況下能夠在初始化階段以後開始,這是爲了支持Java語言的運行時綁定(也成爲動態綁定或晚期綁定)。算法
另外注意這裏的幾個階段是按順序開始,而不是按順序進行或完成,由於這些階段一般都是互相交叉地混合進行的,一般在一個階段執行的過程當中調用或激活另外一個階段。數據庫
站在java虛擬機的角度來說,只存在兩種不一樣的類加載器:啓動類加載器和其餘的加載器。(由於啓動類加載器是使用C++語言實現的,它是虛擬機自身的一部分,而其餘的加載器則是由java語言實現的,並且都是繼承自抽象類java.lang.ClassLoad,而這些加載器必須由啓動類加載器加載到內存後,才能去加載其餘的類。)緩存
可是站在java開發人員來看,類加載器大概分爲四種,啓動類加載器,擴展類加載器,應用程序類加載器,自定義加載器。安全
啓動類加載器(BootStrap ClassLoad):網絡
負責加載存在在jdk/jre/lib目錄下的能被虛擬機識別的類庫,並且啓動類加載器是沒法被java程序直接應用的。數據結構
擴展類加載器(Extension ClassLoad):多線程
該加載器是用來加載jdk/jre/lib/ext目錄下的全部類庫,開發者能夠直接使用擴展類加載器。jvm
應用程序類加載器(Application ClassLoad): 該類加載器是負責加載用戶類路徑所指定的類,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的加載器,那麼這個加載器就是程序中默認的加載器。
自定義類加載器(User ClassLoad):
自定義加載器是用戶本身定義的加載器,這種加載器主要注意三點:
1.在執行非置信代碼以前,自動驗證數字簽名。
2.動態地建立符合用戶特定須要的定製化構建類。
3.從特定的場所取得java class,例如數據庫中和網絡中。
複製代碼
全盤負責,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其餘Class也將由該類加載器負責載入,除非顯示使用另一個類加載器來載入。
父類委託,先讓父類加載器試圖加載該類,只有在父類加載器沒法加載該類時才嘗試從本身的類路徑中加載該類。
緩存機制,緩存機制將會保證全部加載過的Class都會被緩存,當程序中須要使用某個Class時,類加載器先從緩存區尋找該Class,只有緩存區不存在,系統纔會讀取該類對應的二進制數據,並將其轉換成Class對象,存入緩存區。這就是爲何修改了Class後,必須重啓JVM,程序的修改纔會生效。
雙親委派模型的工做流程是:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把請求委託給父加載器去完成,依次向上,所以,全部的類加載請求最終都應該被傳遞到頂層的啓動類加載器中,只有當父加載器在它的搜索範圍中沒有找到所需的類時,即沒法完成該加載,子加載器纔會嘗試本身去加載該類。
雙親委派模型的意義:
1.系統類防止出現內存中出現多份一樣的字節碼。
2.保證java程序安全穩定運行。
複製代碼
Jvm的內存結構主要是由java堆,java棧,本地方法棧,方法區,程序計數器組成。
Java堆內存,是由全部線程共享的一塊內存區域,這個內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存。
從內存回收的角度來說,由於如今的收集器基本都是採用的分代收集算法,因此java堆中還能夠細分爲:新生代和老年代。而新生代,則又進行劃分,分爲了Eden區,From Survivor區域,To Survivor區域。
方法區和java堆同樣,都是由全部線程共享的內存區域。它主要的做用,是存儲已經被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。雖然在java虛擬機規範裏面,會把方法區看成java堆的一個邏輯部分,可是它卻也有一個別名,叫作Non-Heap(非堆),目的是要與java堆分開來。
可是,在HotSpot虛擬機上,會把方法區稱爲「永久代」,本質上固然二者並不等價,而出現這種狀況的緣由是,HotSpot虛擬機的團隊將GC分代收集擴展到了方法區,與java堆聯合在一塊兒,造成了一個GC回收的網絡。
當java文件被打包成class文件的時候,裏面的代碼都是被優化過的,可是java程序是多線程的,當一個線程在執行一個程序到一半的時候,突然被調去執行另一個程序了,那麼如何保證等它執行另一個程序,返回過來執行以前的程序的時候,可以準確的找到以前執行到程序的哪一個點呢?這個時候就須要程序計數器了。
每個線程都會有一個單獨的程序計數器,它會記錄這個線程正在執行的虛擬機字節碼指令的地址,可是隻能記錄java方法的,若是是Native方法,那麼這個計數器的值就是爲空(Undefined)。 這個區域,也是惟一一個在java虛擬機規範中沒有規定任何內存溢出狀況的區域。
和程序計數器同樣,java棧也是線程私有的內存區域。
一個線程,當它開始的時候,就會內存中出現一個獨屬於它的java棧,當這個線程在執行一個方法的時候,java棧中會開闢一個棧幀,這個棧幀將會存儲這個方法的局部變量表,操做棧,動態連接,方法出口等信息。因此線程會調用一個又一個的方法,那麼也就會在獨屬於它的java棧中開闢出一個又一個的棧幀,而每個方法被調用直到這個方法被執行完成,就對應着一個棧幀在虛擬機棧中從入棧到出棧的一個過程。
可是java棧,只爲虛擬機執行java方法服務。
本地方法棧(Native Method Stacks)與虛擬機棧所發揮的做用是很是類似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。虛擬機規範中對本地方法棧中的方法使用的語言、使用方式與數據結構並無強制規定,所以具體的虛擬機能夠自由實現它。
甚至有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧同樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常。
在jvm中,程序計數器,java棧,本地方法棧都是隨着線程而生也隨着線程而滅,那麼天然就實現了內存的清理。可是java堆和方法區,卻不是這樣,所以咱們如今的GC垃圾回收器主要就是集中在java堆和方法區。
判斷對象是否存活通常有兩種方式:
引用計數:每一個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時能夠回收。此方法簡單,沒法解決對象相互循環引用的問題。
可達性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱爲引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。不可達對象。
分代收集算法的前提,是肯定絕大部分對象的生命週期都是很是短暫的,存活時間短。
分代收集算法的特色,是把java堆設置新生代和老年代,這樣就能夠根據不一樣年代的特色來選擇最合適的算法。(如今討論的基本都是HotSpot虛擬機)
這個是java堆中,java堆主要是分爲新生代和老年代,通常默認的比例是新生代佔據整個java堆的1/3,新生代本身也會再度細分,目的是爲了選擇出更爲合適的GC收集算法,提高回收的效率。 新生代分爲了Eden區,From Survivor區域,To Survivor區域。
當系統建立一個對象的時候,老是會在Eden區操做,可是大部分對象,都是在建立後不久就永遠都不會再使用了,所以也會很快變得不可達,當這個區域快要滿了到時候,就會觸發一次YongGC,將這些不可達的對象清理掉。而剩下的還存活着的對象呢,YongGC就會經過「複製算法」,轉移到From Survivor區域。
當From Survivor區域的對象消亡後,會觸發YongGC來清理掉這些對象,而剩下的對象,YongGC則會所有經過「複製算法」複製到To Survivor區域。而這個時候,To Survivor區域就變成了了From Survivor區域,而以前的From Survivor區域則就會被YongGC所有清空內存,變成了To Survivor。 當一個對象,在年輕代存活了足夠長的時間後,若是沒有被GC清理掉,那麼就會經過「複製算法」複製到老年代去。還有一種狀況就是,若是一次GC下來,存活的對象佔據的內存超過了新生代的內存的10%,那麼也會將這部份內存複製到老年代中去。
老年代存儲的對象比年輕代多得多,並且不乏大對象,對老年代進行內存清理時,若是使用中止-複製算法,則至關低效。通常,老年代用的算法是標記-整理(也稱之爲標記-壓縮算法)算法,即標記出仍然存活的對象(存在引用的),將全部存活的對象向一端移動,以保證內存的連續。
在發生Minor GC時,虛擬機會檢查每次晉升進入老年代的大小是否大於老年代的剩餘空間大小,若是大於,則直接觸發一次Full GC。