JVM原理及調優使用總結

一、JVM的內部體系結構:

基本概念:JVM(JAVA虛擬機-java visual machine)由三大部分組成

(1)類裝載器(ClassLoader)子系統
     作用: 用來裝載.class文件
(2)執行引擎
     作用:執行字節碼,或者執行本地方法
(3)運行時數據區
     方法區,堆,java棧,PC寄存器,本地方法棧

---》堆(Heap):是jvm中內存最大的區域,主要存儲的是對象

---》棧(Stack):棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是可以動態變化的,但是在棧中,一個對象只對應了一個4btye的引用(堆棧分離的好處:))。爲什麼不把基本類型放堆中呢?因爲其佔用的空間一般是1~8個字節——需要空間比較少,而且因爲是基本類型,所以不會出現動態增長的情況——長度固定,因此棧中存儲就夠了,如果把他存在堆中是沒有什麼意義的(還會浪費空間)。可以這麼說,基本類型和對象的引用都是存放在棧中,而且都是幾個字節的一個數,因此在程序運行時,他們的處理方式是統一的。但是基本類型、對象引用和對象本身就有所區別了,因爲一個是棧中的數據一個是堆中的數據。最常見的一個問題就是,Java中參數傳遞時的問題 棧的最小單位是棧幀,一個類中含有多個方法,每個方法都對應一個棧幀,多線程中一個線程對應一個棧幀,這樣不同線程之間數據就隔離開了

---》本地方法區(Native Method):執行Native方法或者API時候調用的區域,一般使用的很少

---》方法區(Method Area):是jvm規範裏面的運行時數據區的一個組成部分,jvm規範中的運行時數據區還包含了:pc寄存器、虛擬機棧、堆、方法區、運行時常量池、本地方法棧。

方法區存儲東西?

主要用來存儲class、運行時常量池、字段、方法、代碼、JIT代碼等。

注意:

(1)運行時數據區跟內存不是一個概念。

(2)方法區是運行時數據區的一部分

(3)方法區是jvm規範中的一部分,並不是實際的實現,切忌將規範跟實現混爲一談。

我們再來看下永久帶(Perm區 Permanet Generation):

永久帶又叫Perm區,只存在於hotspot jvm中,並且只存在於jdk7和之前的版本中,jdk8中已經徹底移除了永久帶,jdk8中引入了一個新的內存區域叫metaspace。

(1)並不是所有的jvm中都有永久帶,ibm的j9,oracle的JRocket都沒有永久帶。

(2)永久帶是實現層面的東西。

(3)永久帶裏面存的東西基本上就是方法區規定的那些東西。

因此,我們可以說,永久帶是方法區的一種實現,當然,在hotspot jdk8中metaspace可以看成是方法區的一種實現。

下面我們來看下hotspot jdk8中移除了永久帶以後的內存結構:

https://img2.mukewang.com/5b59198f000159c905450385.jpg

結論:

(1)方法區是規範層面的東西,規定了這一個區域要存放哪些東西

(2)永久帶或者是metaspace是對方法區的不同實現,是實現層面的東西。

---》程序計數器:程序計數器是一塊較小的內存空間,它的作用可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。- - 摘自《深入理解Java虛擬機》

程序計數器的特點:(1)如果線程正在執行的是Java 方法,則這個計數器記錄的是正在執行的虛擬機字節碼指令地址;(2)如果正在執行的是Native 方法,則這個技術器值爲空(Undefined);(3)此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。因爲程序計數器只是改變入棧指令的值,而不會隨着程序的運行計數器所需要的空間而變化。

JVM數據類型
  Java虛擬機中,數據類型可以分爲兩類:基本類型和引用類型。基本類型的變量保存原始值,即:他代表的
值就是數值本身;而引用類型的變量保存引用值。“引用值”代表了某個對象的引用,而不是對象本身,對象
本身存放在這個引用值所表示的地址的位置。

基本類型包括:byte,short,int,long,char,float,double,Boolean,returnAddress
引用類型包括:類類型,接口類型和數組。

棧和堆的區別:棧是運行時的單位,而堆是存儲的單位。棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎麼放、放在哪兒。

二、什麼是垃圾?什麼是垃圾回收器?垃圾回收的算法有哪些?

垃圾? java中使用的是面向對象的類,那麼是不是隻要是沒有引用指向的對象就一定是垃圾呢。答案是否定的,因爲我們使用的對象過程中是有特殊情況的,比如兩個對象相互引用,但是沒有第三個對象指向他們,那麼這種情況下兩個對象有引用指向但是也屬於垃圾,需要被回收,其他還有環形引用,即有多個對象間相互引用,但是最後形成一個環形引用,沒有外界其他對象的引用,所以這種也是垃圾,也是需要被回收的,所以通過引用計數的方式來判斷是否是垃圾的算法不可取,JVM是通過正向可達算法來進行垃圾的判斷的,通過main方法中的任何對象作爲root對象,如果程序中的任何對象對於root可達,那麼就不是垃圾,否則是垃圾。

垃圾回收? GC,即就是Java垃圾回收機制。目前主流的JVM(HotSpot)採用的是分代收集算法。作爲Java開發者,一般不需要專門編寫內存回收和垃圾清理代碼,對內存泄露和溢出的問題。與C++不同的是,Java採用的是類似於樹形結構的可達性分析法來判斷對象是否還存在引用。即:從gcroot開始,把所有可以搜索得到的對象標記爲存活對象。缺點就是:1. 有可能不知不覺浪費了很多內存。2. JVM花費過多時間來進行內存回收。3. 內存泄露

垃圾回收原則:無監控不調優

Java的垃圾回收機制:要從:“什麼時候”,“對什麼東西”,“做了什麼”三個方面來具體分析。
       第一:“什麼時候”即就是GC觸發的條件。GC觸發的條件有兩種。(1)程序調用System.gc時可以觸發;(2)系統自身來決定GC觸發的時機。系統判斷GC觸發的依據:根據Eden區和From Space區的內存大小來決定。當內存大小不足時,則會啓動GC線程並停止應用線程。
       第二:“對什麼東西”籠統的認爲是Java對象。但是準確來講,GC操作的對象分爲:通過可達性分析法無法搜索到的對象和可以搜索到的對象。對於搜索不到的方法進行標記。    
       第三:“做了什麼”最淺顯的理解爲釋放對象。但是從GC的底層機制可以看出,對於可以搜索到的對象進行復制操作,對於搜索不到的對象,調用finalize()方法進行釋放。

       具體過程:當GC線程啓動時,會通過可達性分析法把Eden區和From Space區的存活對象複製到To Space區,然後把Eden Space和From Space區的對象釋放掉。當GC輪訓掃描To Space區一定次數後,把依然存活的對象複製到老年代,然後釋放To Space區的對象。對於用可達性分析法搜索不到的對象,GC並不一定會回收該對象。要完全回收一個對象,至少需要經過兩次標記的過程:第一次標記:對於一個沒有其他引用的對象,篩選該對象是否有必要執行finalize()方法,如果沒有執行必要,則意味可直接回收。(篩選依據:是否複寫或執行過finalize()方法;因爲finalize方法只能被執行一次)。第二次標記:如果被篩選判定位有必要執行,則會放入FQueue隊列,並自動創建一個低優先級的finalize線程來執行釋放操作。如果在一個對象釋放前被其他對象引用,則該對象會被移除FQueue隊列。
 

垃圾回收算法:常用垃圾回收算法有:

1)Mark-Sweep 標記清除法  缺點:內存碎片化,不連續,如果gc後申請大的內存會引起full gc 浪費CPU資源

2)Copying 複製法    缺點:浪費內存

3)Mark-compact 標記壓縮法   缺點:效率比copying稍慢

注意:GC過程中新生代中採用的是from到to的複製算法,因爲一次GC後eden區的對象所剩無幾,此時copying剩下的對象到Survivor0或者Survicor1中效率高也不會浪費太多內存,而年老代採用的是Mark-Compact算法,所以整個jvm的GC採用的是分代蒐集算法,不同代用不通的算法來進行垃圾回收

三、jvm高級篇
 

JVM對象引用類型:

JVM對象分配:

JVM參數及設置:

“-”

“-X”

-XX“”

JVM調優(OutofMemory 與 stackoverflow):

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章