天天看點

java記憶體管理

目錄

  • 一.簡介
  • 二.java記憶體劃分
    • 程式計數器(PC寄存器)
    • Java虛拟機棧
    • 本地方法棧(C棧)
    • 方法區
    • 直接記憶體(堆外記憶體)

可以分幾部分回答這個問題,首先JVM記憶體劃分 | JVM垃圾回收的含義  |  有哪些GC算法  以及年輕代和老年代各自特點等等。

  1. 方法區 (線程共享)  常量  靜态變量  JIT(即時編譯器)編譯後代碼也在方法區存放
  2. 堆記憶體(線程共享) 垃圾回收的主要場地
  3. 程式計數器  目前線程執行的位元組碼的位置訓示器
  4. Java虛拟機棧(棧記憶體) :儲存局部變量,基本資料類型以及堆記憶體中對象的引用變量
  5. 本地方法棧  (C棧):為JVM提供使用native方法的服務

通過這幅圖了解一下

java記憶體管理

JDK 1.8同JDK 1.7 最大的差別是:中繼資料取代了永久代.元空間的本質和永久代類似,都是對JVM規範中的方法區的實作.其元空間和永久代之間的最大差別在于:中繼資料空間不在虛拟機中,而是在本地記憶體中

程式計數器的定義: 程式計數器是一塊較小的記憶體空間,是目前線程正在執行的哪一條位元組碼指令的位址,若目前線程正在執行的是一個本地方法,那麼此時程式計數器為Undefined

程式計數器的作用:

  • 位元組碼解釋器通過改變程式計數器來依次擷取指令,進而實作代碼的流程的控制
  • 在在多線程情況下,程式計數器記錄的是目前線程執行的執行的位置,進而當線程切換回來時,就知道上次線程執行到哪了

程式計數器的特點

  • 是一塊較小的記憶體空間
  • 線程私有,每個線程都有自己的程式計數器
  • 生命周期:随着線程的建立而建立,随着線程的銷毀而銷毀
  • 是一個唯一不會出現的OutOfMemoryError的記憶體區域

定義: 描述Java方法運作過程的記憶體模型

Java虛拟機棧會為每一個即将運作的Java方法建立一塊叫做"棧幀"的區域,用于存放該方法運作過程中的一些資訊,如  局部變量表  /操作數棧  /動态連結 /方法出口資訊

java記憶體管理

壓棧出棧過程

當方法運作過程中需要建立局部變量時,就将局部變量的值存入棧幀的局部變量表中

Java虛拟機棧的棧頂是目前正在執行的活動棧,也就是目前正在執行的方法,PC寄存器也會指向這個位址,隻有這個活動的棧幀的本地變量可以被操作數棧操作,目前這個棧幀中調用另一個方法,與之對應的額棧幀又會被建立,新建立的棧幀壓入棧頂,變成目前的活動棧幀,方法結束後,目前棧幀的傳回值變成新的活動棧幀的中的操作數棧的一個操作數,如果沒有傳回值,那麼新的活動棧幀中操作數棧的操作數沒有變化

由于Java虛拟機棧是線程對應的,資料不是共享的,是以不用關心資料一緻性問題,也不會存在同步鎖的問題

特點

  • 局部變量表随着棧幀的建立而建立,他的大小在編譯時确定,建立時隻需配置設定事先規定的大小即可,在方法運作的過程中,局部變化表的大小不會發生變化
  • Java虛拟機棧會出現兩種異常:StackOverFlowError和OutOfMemoryError
  • StackOverFlowError若Java虛拟機棧的大小不允許動态擴充,那麼目前線程請求的棧的深度超過目前的Java虛拟機棧的最大深度是,就會抛出此異常
  • OutOFMemoryError,若允許動态擴充,那麼目前線程的請求的棧記憶體用完了,無法再動态擴充時,抛出此異常
  • Java虛拟機棧也是線程私有,随着線程建立而建立,随着線程的結束而銷毀

定義: 是為了JVM運作native方法準備的空間,由于很多native方法都是用C語言實作的,是以通常又叫C棧,它與Java虛拟機棧實作的功能類似,隻不過本地方法棧描述本地方法運作過程的記憶體模型

棧幀變化過程:

本地方法被執行時,在本地方法棧也會建立一塊棧幀,用于存放該方法的局部變量表  /操作數棧 /動态連結 /方法出口等資訊; 方法結束後,相應的棧幀也會出棧,并釋放記憶體空間.也會抛出StackOverFlowError和OutOfMemoryError異常

定義: 堆是用來對象的記憶體空間,幾乎所有的對象都存儲在堆中

特點:

  • 線程共享,整個Java虛拟機隻有一個堆,所有線程都通路同一個堆.
  • 在虛拟機啟動時建立
  • 是垃圾回收的主要場地
  • 進一步可分為:新生代(Eden區 From Survior To Surviror)  老年代
  • 不同的區域存放的不同生命周期的對象,這樣可以根據不同區域使用不同的垃圾回收算法,更具有針對性. 堆的大小也可以固定也可以擴充,對于主流的虛拟機,堆大小可擴充的,是以當線程請求配置設定的記憶體,但堆已滿,且記憶體已無法再擴充,就抛出OutOfMemoryError異常

定義: Java虛拟機規範中定義方法區是堆的一個邏輯部分,方法區存放以下資訊  已被虛拟機加載的類資訊  /常量  /靜态變量 /即時編譯後代碼

  • 線程共享.方法區是堆的一個邏輯部分,是以和堆一樣,都是線程共享,整個虛拟機中隻有一個方法區
  • 永久代 方法區中的資訊一般需要長期存在,而且它又是堆的邏輯分區,是以用堆的劃分方法,把方法區稱為"永久代"
  • 記憶體回收的效率低.方法區中的資訊一般需要長期存在,回收一遍隻有少量資訊無效.主要回收的目标是: 對常量池的回收;對類型的解除安裝
  • Java虛拟機規範l對方法區的要求比較寬松,和堆一樣,允許固定大小.也允許動态擴充,還允許不實作垃圾回收

運作時常量池:

方法區中存放:類資訊  常量  靜态變量  即時編譯器變編譯後代碼.常量就存放在運作時常量池中.當類被Java虛拟機加載後,.class檔案中的常量就存在方法區的運作常量池,而且在運作期間,可以向常量池中添加新的常量,如String類的intern()方法就能在運作期間向常量池中添加字元串常量

直接記憶體是除Java虛拟機之外的記憶體,但有可能被Java使用

操作直接記憶體:

在NIO中引入了一種基于通道和緩存的IO方式,他可以調用本地方法的直接配置設定Java虛拟機之外的記憶體,然後通過一個存儲在堆中的DirectByteBuffer對象直接操作該記憶體,而無需将外部記憶體中資料複制到堆中再進行操作,進而提高資料操作的效率,直接記憶體的大小不受Java虛拟機,也會抛出OutOfMemoryError異常

**

直接記憶體和堆記憶體比較:**

  • 直接記憶體申請空間耗費更高的性能
  • 直接記憶體讀取IO的性能優于普通的堆記憶體
  • 直接記憶體的作用鍊:本地IO-->直接記憶體-->本地IO
  • 堆記憶體的作用鍊:本地IO-->直接記憶體-->非直接記憶體-->直接記憶體--->本地IO
  • 伺服器管理者在配置虛拟機參數時,會根據實際記憶體設定 -Xmx等參數資訊,但經常忽略直接記憶體,使得各個記憶體區域總和大于實體記憶體,進而導緻動态擴充時出現OutOFMemoryError

2)類似 -Xms  -Xmn這些參數的含義

堆記憶體配置設定

① : JVM初始配置設定的記憶體由-Xms指定,預設是實體記憶體的1/64

②:  JVM最大配置設定的記憶體由-Xmx指定,預設是實體記憶體的1/4

③: 預設空餘堆記憶體小于40%時,JVM就會增加堆直到-Xmx的最大限制;空餘堆記憶體大于70%時,JVM會減少堆直到-Xms的最小限制

④: 是以伺服器一般設定-Xms  -Xmx相等以避免在每次GC後調整堆大小. 對象的堆記憶體由成為垃圾回收器的自動記憶體管理系統回收

非堆記憶體配置設定:

①:JVM使用-XX:PermSize 設定非堆記憶體的初始值,預設實體記憶體的1/64;

② :由XX:MaxPermSize設定設定最大非堆記憶體的大小

③: -Xmn2G :設定年輕代的大小為2G

④ :-XX:SurvivorRatio ,設定年輕代中Eden區與Survivor區的比值

垃圾回收的算法有哪些?

① 引用計數法:原理是在此對象有個引用,即增加一個計數,删除一個引用則減少一個計數.垃圾回收時,隻收集計數為0的對象.此算法的最緻命的無法處理循環引用的問題

②: 标記-清除 :此算法分兩個階段,第一階段從引用的根節點開始标記所有被引用的對象,第二階段周遊整個堆,把未标記的對象清除,此算法需要暫停應用,同時産生記憶體碎片

③: 複制算法 此算法把記憶體劃分為兩個相等的區域,每次隻使用一個區域,垃圾回收時,周遊目前使用的區域,把正在使用的對象複制到另一個區域中每次算法每次隻處理正在使用的對象,是以複制的成本比較小,同時複制過去以後還能進行相應的記憶體整理,不會出現"碎片問題",此算法的缺點也很明顯,需要兩倍的記憶體空間

④: 标記-整理:此算法結合了"标記-清除"和:複制算法的兩個的優點,也是分兩個階段,第一個階段從根節點開始标記所有被引用對象,第二階段周遊整個堆,清除未标記的對象并且把存活的對象"壓縮"到堆的其中一塊,按順序排放,,此算法避免"标記-清除"的碎片問題,同時也避免"複制"的空間問題

root搜尋算法中,哪些可以作為root?

  • 被啟動類(bootstrap加載器)加載的類和建立的對象
  • JavaStack中引用的對象(棧記憶體中引用的對象)
  • 方法區中靜态引用

本文版權歸作者所有,歡迎轉載,請務必添加原文連結。