JVM入門及調優

jvm是在操作系統之上的,他與硬件沒有直接的交互。

一、JVM體系結構(非常重要)

其中:

1、ClassLoder類加載器相當於快遞員,只負責傳輸,而真正執行的是後面的額執行引擎

2、Jvm調優只能調亮的地方也就是方法區和堆灰色的部分不能調優灰色的都是私有的,不存在垃圾回收和調優

二、類加載器(重點是加載器的雙親委派機制和沙箱機制)

負責加載class文件,但是怎麼識別是不是class文件? class文件有十六進制的特定的文件標識符

幾種系統類加載器

1、Bootstrap,  開始加載常用的類庫主要是rt類庫,裏面包含object等

2、extendsion,擴展這個加載一些擴展的類

3、用戶要想自己定義一個加載器,需要繼承Java.lang.ClassLoader

4、sun.misc.Launcher 它是一個java虛擬機的入口應用

雙親委派機制和沙箱安全機制

兩者是相結合的,雙親委派機制保證了沙箱安全機制,最簡單的例子就是,當自己定義一個同包名同類名的String時,當運行程序時,系統還是用的自帶的,還是可以正常運行。

雙親委派機制就是簡單的說,有事找我爹,我爹叫李剛,解釋一下就是,在運行一個類的時候,先是AppClassLoad運行,一旦遇到類的加載,比如new String的時候,此時,將上交給他的父類,也就是ExtensionClassLoad,此時ExtensionClassLoad也不處理,在交給BootstrapClassLoad處理,此時如果說在Bootstrap裏面加載的類找到了想要的類就直接加載並引用並不在往下,如果是沒有則接着往下找,找到就停止,

  這種有事往上找,類加載往下走的機制,增強了Java安全性,使得比如自己定義的Stringutil等不被加載,只加載系統的string,避免了這種污染。

  比如。自己定義java.lang.String這個類,結果在其他引用String這個類的地方用的還是系統的,自己定義的沒有被加載,這就是沙箱安全,保證了java不被這樣代碼污染。

三、PC寄存器

相當於值日表,其實裏面存的是調度指令集,爲什麼方法在調用的時候沒有出現過錯誤呢?就是因爲pc寄存器按棧的順序存了指令,實際上就是地址。

每個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區中的方法字節碼(用來存儲指向下一條指令的地址,也即將要執行的指令代碼),由執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不記。

這塊內存區域很小,它是當前線程所執行的字節碼的行號指示器,字節碼解釋器通過改變這個計數器的值來選取下一條需要執行的字節碼指令。就相當於try catch,如果出現異常,就會改變值使得進入catch裏面

如果執行的是一個Native方法,那這個計數器是空的。

用以完成分支if,循環while等,跳轉if等,異常處理,線程恢復等基礎功能,不會發生內存溢出(OOM)錯誤。

四、方法區

方法區裏面裝的是類模板,就是大Class存放的地方,方法區是被所有線程共享,此區屬於共享區間,所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義

方法區 =  靜態變量+常量+類信息(構造方法/接口定義)+運行時常量池存在方法區中

But   實例變量存在堆內存中,和方法區無關

五、棧

棧管運行,堆管存儲

棧主要是用來方法運行的,他的機制先進後出的特性也使得他適合方法運行,那麼一般而言,棧底什麼方法呢?就是main方法,他是第一個進棧的,也是最後出站的,而棧頂就是當前方法。

1、詳細解釋棧:

棧也叫棧內存,主管Java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束該棧就Over,生命週期和線程一致,是線程私有的。存儲什麼?8基本類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配。

2、棧存儲什麼? 簡單的說就是方法的信息

棧幀中主要保存3 類數據:

本地變量(Local Variables):輸入參數和輸出參數以及方法內的變量;

棧操作(Operand Stack:記錄出棧、入棧的操作;

棧幀數據(Frame Data:包括類文件、方法等等。

3、常報異常:

Exception in thread "main" java.lang.StackOverflowError

六、++方法區的交互關係

七、堆(唯一可調機制)

1、堆內存邏輯上分爲三部分:新生+養老+永久 

 物理上是:新生+ 養老    爲什麼沒有永久區?見後面解釋

 倖存0區和倖存1區是隨時互換的,條件就是,當哪個區裏面有數據就是倖存0區

大部分的存儲都是在新生區中進行的,新生區主要有以下功能

新生區是類的誕生、成長、消亡的區域,一個類在這裏產生,應用,最後被垃圾回收器收集,結束生命。新生區又分爲兩部分: 伊甸區(Eden space)和倖存者區(Survivor pace) ,所有的類都是在伊甸區被new出來的。倖存區有兩個: 0區(Survivor 0 space)和1區(Survivor 1 space)。當伊甸園的空間用完時,程序又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收(Minor GC),將伊甸園區中的不再被其他對象所引用的對象進行銷燬。然後將伊甸園中的剩餘對象移動到倖存 0區。若倖存 0區也滿了,再對該區進行垃圾回收,然後移動到 1 區。那如果1 區也滿了呢?再移動到養老區。若養老區也滿了,那麼這個時候將產生MajorGCFullGC),進行養老區的內存清理。若養老區執行了Full GC之後發現依然無法進行對象的保存,就會產生OOM異常“OutOfMemoryError

2、爲什麼沒有永久區?簡單一句話就是永久代實際上方法區的一個實例,不屬於堆內存

實際而言,方法區(Method Area)和堆一樣,是各個線程共享的內存區域,它用於存儲虛擬機加載的:類信息+普通常量+靜態常量+編譯器編譯後的代碼等等,雖然JVM規範將方法區描述爲堆的一個邏輯部分,但它卻還有一個別名叫做Non-Heap(非堆),目的就是要和堆分開。

  對於HotSpot虛擬機,很多開發者習慣將方法區稱之爲“永久代(Parmanent Gen) ,但嚴格本質上說兩者不同,或者說使用永久代來實現方法區而已,永久代是方法區(相當於是一個接口interface)的一個實現,jdk1.7的版本中,已經將原本放在永久代的字符串常量池移走。

如果出現java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機對永久代Perm內存設置不夠。一般出現這種情況,都是程序啓動需要加載大量的第三方jar包。例如:在一個Tomcat下部署了太多的應用。或者大量動態反射生成的類不斷被加載,最終導致Perm區被佔滿。

Jdk1.6及之前: 有永久代, 常量池1.6在方法區

Jdk1.7:       有永久代,但已經逐步“去永久代”,常量池1.7在堆

Jdk1.8及之後: 無永久代,常量池1.8在元空間

八、GC

minor gc是我們常說的gc,他是作用在新生區的伊甸區的,是一個輕量級的gc,一般是伊甸區中new太多數據,當超過70%的時候就啓動gc進行殺死,

major gc 就是一種重量級的垃圾回收機制了,當經過伊甸區的清理後倖存到倖存區1和倖存區2或者是養老區,當這些的區的數據太多的時候也會啓動gc,否則會報OOM異常

1、Java7

2、Java8

 JDK 1.8之後將最初的永久代取消了,由元空間取代。

3、堆內存調優

 

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