記憶體結構

西涼馬戳戳發表於2020-11-18

記憶體結構

程式計數器(暫存器)

作用:記住下一條 jvm 指令的執行地址

特點:

  • 是執行緒私有的

    為了執行緒切換後能恢復到原來的執行位置,每條執行緒都需要有一個獨立的程式計數器,各個計數器之間互不影響。

  • 不會存在記憶體溢位

虛擬機器棧

定義

Java Virtual Machine Stacks (Java 虛擬機器棧)

  • 每一個執行緒都會開闢一個虛擬機器棧,用於存放棧幀;棧幀用於儲存區域性變數表、運算元棧、動態連線和方法出口等資訊。

    區域性變數表存放了編譯期可見的各種基本資料型別,這些資料型別在表中的儲存空間以區域性變數槽(slot)表示,

  • 每個執行緒只能有一個活動棧幀,對應著當前正在執行的那個方法。

  • 如果執行緒請求的棧深度大於虛擬機器所允許的深度,丟擲SO異常;如果棧擴充套件無法申請到足夠的記憶體丟擲OOM異常。

問題 ?

  1. 垃圾回收是否涉及棧記憶體?

    不是,棧記憶體隨著方法執行的結束而釋放,並不會被垃圾回收釋放,垃圾回收釋放的堆記憶體。

  2. 棧記憶體分配越大越好嗎?

    不是,棧記憶體分配越大,會導致執行緒數量減少。

  3. 方法內的區域性變數是否執行緒安全?

  • 如果方法內區域性變數沒有逃離方法的作用範圍,它是執行緒安全的
  • 如果是區域性變數引用了物件,或逃離方法的作用範圍,需要考慮執行緒安全

棧記憶體溢位

  • 棧幀過多導致棧記憶體溢位
  • 棧幀過大導致棧記憶體溢位

執行緒執行診斷

1、cpu 佔用過多

  • 用top定位哪個程式對cpu的佔用過高

  • ps H -eo pid,tid,%cpu | grep 程式id (用ps命令進一步定位是哪個執行緒引起的cpu佔用過高)

  • jstack 程式id

    可以根據執行緒id 找到有問題的執行緒,進一步定位到問題程式碼的原始碼行號

2、程式執行很長時間沒有結果

本地方法棧

呼叫本地方法和作業系統互動,例如Object 裡的方法,方法前有 native 修飾。

定義

通過 new 關鍵字,建立物件都會使用堆記憶體

特點

  • 它是執行緒共享的,堆中物件都需要考慮執行緒安全的問題
  • 有垃圾回收機制

堆記憶體溢位

垃圾回收會對無用的物件進行回收,但如果一直有物件被建立,並且物件一直被使用,最終會導致堆記憶體溢位。

堆記憶體診斷

1、 jps 工具

檢視當前系統中有哪些 java 程式

2、map 工具

檢視堆記憶體佔用情況

 jhsdb jmap --heap --pid 程式id

3、jconsole 工具

圖形介面的,多功能的監測工具,可以連續監測

案例

  • 垃圾回收後,記憶體佔用仍然很高

    開啟jdk按照目錄:jvisualvm.exe

方法區

方法區記憶體溢位

  • 1.8前會導致永久代溢位
  • 1.8後會導致元空間溢位

執行時常量池

  • 常量池就是一張表,虛擬機器指令根據這張表找到要執行的類名、方法名、引數型別、字面量等資訊
  • 執行時常量池,當該類被載入時,它的常量池資訊會放入執行時常量池,並把裡面的符號地址變為真實地址

StringTable

  • 常量池中的字串僅僅是符號,只有第一次用到時才變為物件
  • 利用串池的機制,避免重複建立字串物件
  • 字串變數拼接的原理是StringBuilder
  • 字串常量拼接的原理是編譯器優化
  • 可以使用intern 方法,主動將串池中還沒有的字串物件加入串池
    • 1.8 將這個字串物件嘗試放入串池,如果有則不放入,沒有則放入,返回串池中的物件
    • 1.6 將這個字串物件嘗試放入串池,如果有則不放入,沒有則複製一份此物件,放入串池,返回串池中的物件

StringTable 調優:

  • -Xms500m -Xmx500m -XX:PrintStringTableStatistics -XX:StringTableSize =

    -jvm啟動時分配的最大記憶體 -jvm執行過程中分配的最大記憶體 -列印串池中的統計資訊 -設定串池中桶的個數

  • 有大量的重複的字串,可以讓字串入池,減少字串的個數,減少堆記憶體的使用。

直接記憶體

  • 常見於NIO操作,用於資料緩衝區
  • 分配回收成本高,但讀寫效能高
  • 不受jvm記憶體回收管理

直接記憶體釋放原理

  • 使用了Unsafe 物件完成直接記憶體的分配回收,並且回收需要主動呼叫freeMemory方法
  • ByteBuffer 的實現類內部,使用了 Cleaner(虛引用) 來監測 ByteBuffer 物件,一旦 ByteBuffer 物件被垃圾回收,那麼就會由ReferenceHandler 執行緒通過 Cleaner 的 clean 方法呼叫 freeMemory 來釋放直接記憶體

-XX:+DisableExplictGC 關閉顯示的gc:System.gc()

相關文章