JVM學習筆記(一)--初步接觸JVM

Java平臺無關性的實現

Java源文件被編譯成能被Java虛擬機執行的字節碼文件。 Java被設計成允許應用程序可以運行在任意的平臺,而不需要程序員爲每一個平臺單獨重寫或者是重新編譯。它屏蔽了不同操作系統版本之間的差異,只需要選擇不同位數和版本的虛擬機,Java代碼即可運行在對對應的操作系統

JVM中內存的劃分

在這裏插入圖片描述
Java源文件經過jdk的編譯,生成字節碼文件,即class文件,然後通過類加載器Class Loader,將其加載到運行時數據區,即JVM中,進行一系列的操作。

  • 方法區:各個線程共享的內存區域,用於存儲已經被類加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
  • 程序計數器:一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。
  • 堆內存:是Java虛擬機所管理的內存中最大的一塊,也是垃圾回收的主要場所。Java堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。
  • Java棧:棧內存中主要保存局部變量、基本數據類型變量以及堆內存中某個對象的引用變量。每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表,操作數棧,動態鏈接,方法出口等信息。棧中的棧幀隨着方法的進入和退出有條不紊的執行着出棧和入棧的操作。
  • 本地方法棧:主要是爲JVM提供使用native 方法的服務。

內存分配與垃圾回收

在這裏插入圖片描述
JVM的內存可以分爲堆內存和非堆內存。堆內存分爲年輕代和老年代。年輕代又可以進一步劃分爲一個Eden(伊甸)區和兩個Survivor(倖存)區組成

對象創建過程中的內存分配

一般通過new指令來創建對象,當虛擬機遇見一條new指令,會去檢查這個指令的參數是否能在常量池中定位到某個類的符號引用,並且檢查這個符號引用代表的類是否已經被加載,解析和初始化。如果沒有,那麼會執行類加載過程。通過執行類的加載,驗證,準備,解析,初始化步驟,完成了類的加載,這個時候會爲該對象進行內存分配,也就是把一塊確定大小的內存從Java堆中劃分出來,在分配的內存上完成對象的創建工作。

內存分配的兩種方式

  • 指針碰撞方式:假設Java堆中的內存是絕對規整的,用過的內存在一邊,未使用的內存在另一邊,中間有一個指示指針,那麼所有的內存分配就是把那個指針向空閒空間那邊挪動一段與對象大小相等的距離
  • 空閒列表方式:如果Java堆內存中不是規整的,已使用和未使用的內存相互交錯,那麼虛擬機就必須維護一個列表用來記錄哪塊內存是可用的,在分配的時候找到一塊足夠大的空間分配對象實例,並且需要更新列表上的記錄。

垃圾回收

JVM如何判定一個對象是否應該被回收

判斷一個對象是否應該被回收,主要是看其是否還有引用。判斷對象是否存在引用關係的方法包括引用計數法以及root根搜索方法。

  • 引用計數法:原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只需要收集計數爲0的對象。此算法最致命的是無法處理循環引用的問題。
  • root根搜索方法:root搜索方法的基本思路就是通過一系列可以做爲root的對象作爲起始點,從這些節點開始向下搜索。當一個對象到root節點沒有任何引用鏈接時,則證明此對象是可以被回收的。以下對象會被認爲是root對象:
  1. 棧內存中的引用對象
  2. 方法區中靜態引用和常量引用指向的對象
  3. 被啓動類加載的類和創建的對象

垃圾回收算法

標記清除算法

標記-清除算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。此算法需要暫停整個應用,並且會產生內存碎片。

複製算法

複製算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另外一個區域中。複製算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的內存整理,不會出現“碎片”問題。當然,此算法的缺點也是很明顯的,就是需要兩倍內存空間。

標記整理算法

標記-整理(Mark-Compact)算法不直接對可回收對象進行清理,而是讓所有可用的對象都向一端移動。然後直接清理掉邊界以外的內存。
在這裏插入圖片描述

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