從HelloWord學習JVM

JVM虛擬機

一、爲什麼學習JVM

面試、找工作、OOM、調優

二、什麼是JVM,它做了什麼

java虛擬機,執行java代碼的平臺,屏蔽了底層硬件指令的細節,一次編寫到處執行

代碼執行過程:源代碼->字節碼文件class->-->jvm->

jvm&jdk&jre 關係  jdk包括jre和jvm

jvm做了什麼?

1,空間分配回收,而c++需要考慮內存分配和回收。java能讓開發者100%精力投入業務開發

2,內存管理

3,屏蔽底層硬件區別

 

三、jvm運行時運行區

1,程序計數器:指向當前線程執行的字節碼指令的地址,行號,如0x3d48

java最小的執行單位是線程,但線程只負責做,做什麼需要指令告知;

程序計數器爲什麼要記錄地址呢?因爲cpu是有調度策略的,當cpu被其他線程搶到了,此時該線程被掛起,此時需要記錄上一次執行到的地址方便線程恢復執行。

 

2,虛擬機棧(FILO先進後出):存儲當前線程運行方法時所需要的數據,指令和返回地址。

如一個helloword()方法,一個方法一個棧幀,局部變量表存儲局部變量,一個程序運行時就能確定的空間,一個棧層存儲32位的數,即一個int,多個字節要拆開存儲如64位拆爲高位和地位。

操作數棧:比如算sum=i+j  從局部變量表加載i,j,然後做加法得到sum,再將sum存到局部變量表,即對應4條指令。

注意:成員變量不管是不是引用類型都存儲在堆內存中的。

但對於引用類型obj。局部變量表只記錄obj的地址應用,所以結論棧指向堆。

學會看反編譯字節碼文件,javap -v HelloWord.class > aa.txt 參考javap指令集查看https://www.cnblogs.com/JsonShare/p/8798735.html

如methodOne(int i){int j=0; } ,僅一個int j=0 對應兩條指令;  j存在局部變量第二位置裏,地址0:存this 地址一1:存 i  地址2:存j。

 

動態鏈接:java動態特性,如運行時多態,如@Autowired private Service service;

代碼調用serevice.do()。需動態解析,到底是哪個實現類的do方法。爲什麼動態鏈接要存在棧幀裏呢?

方法出口:即return,正常出口和異常出口如throw。

一個方法調用另一個方法,怎麼入棧?先入棧methodOne,再入棧methodTwo

 

3,本地方法棧:native方法棧,

4,方法區:存儲類信息即class文件,常量,靜態變量,JIT,methodOne存儲在方法區。

5,堆內存:分代模型 就分新生代和老年代,跟後面的永久代和元空間沒關係。

注意:jdk1.8後 永久代變成了元空間meta space主要解決永久代溢出的問題,因爲元空間會自擴容,跟arraylist差不多。

方法區就存在永久區。老年代默認是新生代的2倍。創建對象首先在eden區詢問空間,有則放下,沒有則觸發yonggc ,把之前的對象移動到surivor區域的from區域,如果from也放不下,就會觸發擔保機制,直接放到老年代,每新生代gc一次,就觸發from和to的交換一次,對象向下移動年齡是加1的。

Xms  s:starting堆初始的總大小

Xmn  n:new  堆初始時new空間的大小

Xmx   x:max堆最多的空間

 

垃圾回收算法:gc算法:複製回收算法(新生代用的),標記清除算法和標記內存整理算法(後面倆是老年代用的)。

垃圾回收器:不同的垃圾回收器是實現了不同的垃圾回收算法的,如parllen new等實現了

複製回收算法,eden區一定是空的。

 

 

內存溢出後排查是否是內存泄露。

對象的生命週期,什麼對象能被回收,即是否可達,gc root概念,當一個對象被應用時計數器就加1,當爲0時即無引用代表可回收。堆裏有很多gc root只要有gcroot指向的都是可達的,都不能回收。

gc root有哪些:有4種;強應用,弱引用。。。

 

FullGc 5分鐘一次怎麼調優? 衡量的標準和維度?5分鐘一次怎麼啦?是要追求吞吐率還是最小執行時間?業務出現性能問題,90%以上是業務調優,並不是有問題就調jvm。

內存泄露,要找出gcroot,即泄露源,因爲它指向了很多對象,或者死循環對象,被回收不了。

性能調優:發揮機器本來的性能。

1,如果追求吞吐率就要算吞吐率,看下回收器用的什麼,如可用cms就是追求吞吐率的。

2,

 

 

總結:程序計數器,虛擬機棧,本地方法棧是線程獨有的,每個線程都有,但堆和方法區是共有的。

 

四:源碼-》字節碼-》裝載-》鏈接-》初始化

 

裝載:怎麼裝載的,用類加載器 1,通過類的全限定名找到類的二進制流;2,將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構;

3,在java堆中生成一個java.lang.calss對象,作爲方法區中這些數據的入口;

鏈接:驗證,保證類加載的正確性,如文件格式,符號引用等;準備,爲類的靜態變量分配內存,並將其初始化爲默認值如private static int a=10,將a初始化爲0而不是10:解析,把類中的符號引用轉換爲直接引用;

4,初始化:對類的靜態變量或靜態代碼塊執行初始化操作;這時a才初始化爲10;

五:類加載機制&類加載器

bootstrap classloader 負責加載jre/lib/rt.jar

extension classloader 加載javahome中的jre/lib/*.jar

app classloader 負責加載clsspath下的類和jar包

custom classloader 自定義加載類,實現ClassLoadeer

雙親委派機制:所有類加載原則,都是回溯到父類去加載,一直到最頂層加載,沒有再交給孩子節點加載,保證同一個類只有一個層級加載;

六、運行時數據區 共6塊

兩塊:一個是跟隨線程生命週期的如虛擬機棧,本地方法棧,程序計數器;另一個是跟隨jvm生命週期的如堆方法區

方法區:

常量final修飾的,靜態變量static修飾的;方法區是線程非安全的,因爲共享;

堆:所有線程共享,對象和數組

虛擬機棧:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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