天天看點

記憶體結構

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

特點:

是線程私有的

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

不會存在記憶體溢出

Java Virtual Machine Stacks (Java 虛拟機棧)

每一個線程都會開辟一個虛拟機棧,用于存放棧幀;棧幀用于存儲局部變量表、操作數棧、動态連接配接和方法出口等資訊。

局部變量表存放了編譯期可見的各種基本資料類型,這些資料類型在表中的存儲空間以局部變量槽(slot)表示,

每個線程隻能有一個活動棧幀,對應着目前正在執行的那個方法。

如果線程請求的棧深度大于虛拟機所允許的深度,抛出SO異常;如果棧擴充無法申請到足夠的記憶體抛出OOM異常。

問題 ?

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

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

棧記憶體配置設定越大越好嗎?

不是,棧記憶體配置設定越大,會導緻線程數量減少。

方法内的局部變量是否線程安全?

如果方法内局部變量沒有逃離方法的作用範圍,它是線程安全的

如果是局部變量引用了對象,或逃離方法的作用範圍,需要考慮線程安全

棧幀過多導緻棧記憶體溢出

棧幀過大導緻棧記憶體溢出

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 工具

檢視堆記憶體占用情況

3、jconsole 工具

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

案例

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

打開jdk按照目錄:jvisualvm.exe

記憶體結構

1.8前會導緻永久代溢出

1.8後會導緻元空間溢出

常量池就是一張表,虛拟機指令根據這張表找到要執行的類名、方法名、參數類型、字面量等資訊

運作時常量池,當該類被加載時,它的常量池資訊會放入運作時常量池,并把裡面的符号位址變為真實位址

常量池中的字元串僅僅是符号,隻有第一次用到時才變為對象

利用串池的機制,避免重複建立字元串對象

字元串變量拼接的原理是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()