程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java JVM原理與調優_動力節點Java學院整理

Java JVM原理與調優_動力節點Java學院整理

編輯:關於JAVA

Java JVM原理與調優_動力節點Java學院整理。本站提示廣大學習愛好者:(Java JVM原理與調優_動力節點Java學院整理)文章只能為提供參考,不一定能成為您想要的結果。以下是Java JVM原理與調優_動力節點Java學院整理正文


JVM是一種用於計算設備的規范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。Java虛擬機包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域。 JVM屏蔽了與具體操作系統平台相關的信息,使Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平台上不加修改地運行。是運行Java應用最底層部分。

JDK(Java Development kit)

整個Java的核心,包括了Java運行環境(Java Runtime Envirnment),一堆Java工具(編譯,debug等)和Java基礎的類庫(rt.jar)。是開發java應用的基礎。

JRE(Java Runtime Environment,Java運行環境)

運行JAVA程序所必須的環境的集合,包含JVM標准實現及Java核心類庫。運行java應用的基礎。

J2SE(Java 2 Platform,Standard Edition)。

包含那些構成Java語言核心的類。比如:數據庫連接、接口定義、輸入/輸出、網絡編程

J2EE(Java 2 Platform,Enterprise Edition)。

Enterprise Edition(企業版) J2EE 包含J2SE 中的類,並且還包含用於開發企業級應用的類。比如:EJB、servlet、JSP、XML、事務控制。

主要JVM

首先,JVM是一套規范。很多公司均實現了各自的虛擬機。常見的有

HotSpot JVM(sun)
Jrockit JVM(BEA公司的JVM,應用於weblogic)
IBM JVM
Apache Harmony

其中,我們常用的是HotSpot JVM.

JVM結構

第一步(編譯):

創建完源文件之後,程序會先被編譯為.class文件。Java編譯一個類時,如果這個類所依賴的類還沒有被編譯,編譯器就會先編譯這個被依賴的類,然後 引用,這個有點象make。如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話 報“cant find symbol”的錯誤。

第二步(運行):

Java類運行的過程大概可分為兩個過程:1、類的加載  2、類的執行。

需要說明的是:JVM主要在程序第一次主動使用類的時候,才會去加載該類。也就是說,JVM並不是在一開始就把一個程序就所有的類都加載到內存中,而是到不得不用的時候才把它加載進來,而且只加載一次。

1、 在 編譯好java程序得到MainApp.class文件後,在命令行上敲java AppMain。系統就會啟動一個jvm進程,jvm進程從classpath路徑中找到一個名為AppMain.class的二進制文件,將 MainApp的類信息加載到運行時數據區的方法區內,這個過程叫做MainApp類的加載。

2、 (java命令)然後JVM找到AppMain的主函數入口,開始執行main函數

3、 (類加載器)執行過程中,會創建對象。JVM會首先從方法區加載類信息和相關常量,class加載完畢之後,在堆上為對象分配內存,然後調用初始化實例,當然這時候實例保持指向class類型信息,這個信息保存在方法區中。

4、 (執行引擎)調用實例方法時,會根據引用找到對象信息,進而可定位對應的class類型信息,和方法表。

5、 (執行引擎)執行方法時,在虛擬機棧中進行,分配棧幀,隨著入棧出棧,完成方法調用操作。

執行引擎

運行Java的每一個線程都是一個獨立的虛擬機執行引擎的實例。從線程生命周期的開始到結束,他要麼在執行字節碼,要麼在執行本地方法。一個線程可能通過解釋或者使用芯片級指令直接執行字節碼,或者間接通過JIT執行編譯過的本地代碼。我們上文講到的main函數,也就是執行引擎的操作入口。

Class文件

實際上,Class文件中方法的字節碼流就是有JVM的指令序列構成的。每一條指令包含一個單字節的操作碼,後面跟隨0個或多個操作數。

iload_0    // 把存儲在局部變量區中索引為0的整數壓入操作數棧。
iload_1    // 把存儲在局部變量區中索引為1的整數壓入操作數棧。
iadd    // 從操作數棧中彈出兩個整數相加,在將結果壓入操作數棧。
istore_2   // 從操作數棧中彈出結果

JVM運行時數據區

1)程序計數器(線程私有)

當前線程所執行的字節碼的行號指示器,通過改變這個計數器的值,確定下一條要執行的命令。分支,循環,跳轉都需要它的支持。

它是線程私有的,每個線程都有專屬於自己的程序記數器,線程之間互不影響,獨立存儲,保證了線程切換後,可以恢復到原先執行位置。

2)Java虛擬機棧(線程私有)

每個方法的執行,同時都會在虛擬機棧上創建一個棧幀。用於存儲局部變量表,操作數棧,方法出口,動態鏈接等。一個方法的執行周期,同時也就對應著棧幀的出棧入棧操作。有時候方法的遞歸,會造成大量的棧幀,達到一定的深度,會報StackOverflowError異常。有一點需要說明:在編譯器編譯Java代碼時,就已經在字節碼中為每個方法都設置好了局部變量區和操作數棧的數據和大小。並在JVM首次加載方法所屬的Class文件時, 就將這些數據放進了方法區。因此在線程調用方法時,只需要根據方法區中的局部變量區和操作數棧的大小來分配一個新的棧幀的內存大小,並堆入Java棧。

局部變量區: 用來存放方法中的所有局部變量值,包括傳遞的參數。這些數據會被組織成以一個字長(32bit或64bit)為單位的數組結構(以索引0開始)中。其中類 型為int, float, reference(引用類型,記錄對象在堆中地址)和returnAddress(一種JVM內部使用的基本類型)的值占用1個字長,而byte, char和shot會擴大成1個字長存儲,long,double則使用2個字長。

 操作數棧: 用來在執行指令的時候存儲和使用中間結果數據。

幀數據區: 常量池的解析,正常方法返回以及異常派發機制的信息數據都存儲在其中。

3)本地方法棧(線程私有)

與Java虛擬機棧類似,只不過該區域是為native方法提供服務。

4)方法區(Perm)(線程共享)

    存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯後的代碼等數據。包含運行時常量池,用於存放編譯器生成的各種字面量和符號引用,這部分內容是在類加載後進入方法區運行時常量池中。

5)堆

堆是整個內存數據區最負責的部分,負責對象的創建。同時,垃圾回收的主要工作也在於此。堆又進一步進行細分,主要是為了滿足垃圾回收。

堆的組成

Eden(伊甸園):對象創建的入口。

Survivor Space:用於保存在eden space內存池中經過垃圾回收後沒有被回收的對象,也就是“幸存還活著”的對象。

幸存者0區(Survivor 0 space)和幸存者1區(Survivor1 space):當伊甸園的空間用完時,程序又需要創建對象;此時JVM的垃圾回收器將對伊甸園區進行垃圾回收,將伊甸園區中的不再被其他對象所引用的對象 進行銷毀工作。同時將伊甸園中的還有其他對象引用的對象移動到幸存者0區。幸存者0區就是用於存放伊甸園垃圾回收時所幸存下來的JAVA對象。

當將伊甸園中的還有其他對象引用的對象移動到幸存者0區時,如果幸存者0區也沒有空間來存放這些對象時,JVM的垃圾回收器將對幸存者0區進行垃圾 回收處理,將幸存者0區中不在有其他對象引用的JAVA對象進行銷毀,將幸存者0區中還有其他對象引用的對象移動到幸存者1區。幸存者1區的作用就是用於 存放幸存者0區垃圾回收處理所幸存下來的JAVA對象。

Tenured :對象經過survivor 1 space內存池,每經歷過一次垃圾回收,年齡就增加1,超過設定閥值後,被移入終身代,當然也包括由於擔保機制移入的對象。對於新生代和老年代,垃圾回收器對其態度不同。發生在新生代的回收頻率頻繁,大部分對象是“朝生夕死”,收集算法一般采用高效簡單的復制算法,也就是上文描述的對象轉移操作(Eden->survivor 0,survivor 0->survivor 1)。發生在該區域的垃圾回收為Young GC.對於老年代,由於大部分對象主要為存活率高的對象,垃圾回收器采用”標記-整理“算法。發生在該區域的垃圾回收為FULL GC.

堆相關參數

(影響堆空間劃分,進而會影響GC發生頻率)JVM調優工作,主要是基於這些參數,進行適當調整管理,達到調整堆內存大小及比例大小,以滿足實際業務需求。另外還包括方法區。

-Xms:設置 Java 應用程序啟動時的初始堆大小;
-Xmx:設置 Java 應用程序能獲得的最大堆大小;
-Xss:設置線程棧的大小;
-XX:MinHeapFreeRatio:設置堆空間最小空閒比例。當堆空間的空閒內存小於這個數值時,JVM 便會擴展堆空間;
-XX:MaxHeapFreeRatio:設置堆空間的最大空閒比例。當堆空間的空閒內存大於這個數值時,便會壓縮堆空間,得到一個較小的堆;
-XX:NewSize:設置新生代的大小;
-XX:NewRatio:設置老年代與新生代的比例,它等於老年代大小除以新生代大小;
-XX:SurvivorRatio:新生代中 eden 區與 survivor 區的比例;
-XX:MaxPermSize:設置最大的持久區大小;
-XX:TargetSurvivorRatio: 設置 survivor 區的可使用率。當 survivor 區的空間使用率達到這個數值時,會將對象送入老年代。

對象的生命周期

  創建階段

1、檢查指令的參數,是否能在常量池中定位到一個類的符號引用,如果是引用,判斷代表的類是否加載,解析和初始化過

2、如果沒有加載,則必須進行加載,解析和初始化

3、類加載檢查,這時候已經知道所需內存的大小。

4、分配內存。從java堆中劃分一塊大小確定的內存。支持2種方式,至於選擇哪種方式分配內存,與java堆是否規整有關(也就是是否空間空間和使用空間相互交錯情況)。1.指針碰撞(分界點的指示器移動);2.空閒列表方式。然而,java堆是否規整,則取決於垃圾收集器的工作方式。此外,在分配內存時還要考慮多線程情況,保證原子性。分配內存的原子性有2種方式進行保證(CAS 和 本地線程分配緩沖-XX +/- UseTLAB)。

5)、分配內存完成後,初始化內存空間(初始化為0)

6、維護對象的對象頭信息。如元數據信息,哈希碼,GC分代年齡,鎖信息,類元指針。

7、調用init方法,按照程序員意願進行初始化。

     <7.1> 從超類到子類對static成員進行初始化;
     <7.2> 超類成員變量按順序初始化,遞歸調用超類的構造方法;
<7.3> 子類成員變量按順序初始化,子類構造方法調用。

應用階段

分為強引用、軟引用、虛引用、若引用

   不可視階段;

當一個對象處於不可視階段,說明我們在其他區域的代碼中已經不可以在引用它,其強引用已經消失,例如,本地變量超出了其可視的范圍。

   不可到達階段;

處於JVM對象生命周期不可到達階段的對象,在虛擬機所管理的對象引用根集合中再也找不到直接或間接的強引用,這些對象通常是指所有線程棧中的臨時變量, 所有已裝載的類的靜態變量或者對本地代碼接口(JNI)的引用。這些對象都是要被垃圾回收器回收的預備對象,但此時該對象並不能被垃圾回收器直接回收。其 實所有垃圾回收算法所面臨的問題是相同的——找出由分配器分配的,但是用戶程序不可到達的內存塊。

   可收集階段、終結階段、釋放階段 ;

當一個對象處於可收集階段、終結階段與釋放階段時

<1> 回收器發現該對象已經不可達。 

     <2> finalize方法已經被執行。 

     <3> 對象空間已被重用。

相關閱讀:

JVM(Java虛擬機)簡介(動力節點Java學院整理)

以上所述是小編給大家介紹的Java JVM原理與調優_動力節點Java學院整理,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對網站的支持!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved