1. JVM的記憶體區域
JVM將記憶體分為五塊區域分別是程式計數器、虛擬機器棧、本地方法棧、堆和方法區
1.1 程式計數器
定義:
唯一一個不會發生記憶體溢位異常的記憶體區域.
儲存的是當前JVM直譯器執行命令的行號
最小的一塊區域
1.2. 虛擬機器棧
定義: 儲存的是區域性變數表等資訊
區域性變數表
區域性變數表
- reference
- 基本型別變數
- returnAddress
1.3. 本地方法棧
和虛擬機器棧差不多,儲存的是本地方法的資訊,在hotspot虛擬機器中將虛擬機器棧和本地方法棧合二為一
1.4. 堆
定義:
最大的一塊記憶體區域.
存放的是物件的例項資料
JVM啟動時建立
分為新生代、老年代
- 新生代
- 老年代
元空間
和永久代差不多,將class檔案的後設資料儲存到元空間,元空間使用的是本地記憶體
1.5. 方法區
執行時常量池
定義: 存放的是class檔案的常量池的資料
- 常量
- 靜態變數
- JIT及時編譯後的程式碼
類資訊
2. 垃圾收集演算法&垃圾收集器
介紹常用的垃圾收集演算法和垃圾收集器
2.1 判斷物件存活的演算法
引用計數
定義: 只作為了解,沒有虛擬機器使用這種方式.
有物件引用就+1,物件不引用就-1,計數器為0就回收物件.
缺點: 無法解決物件迴圈引用問題
可達性分析
定義: 只要GCRoots連線物件的例項,物件就不能被回收
- 虛擬機器棧引用的物件
- 方法區靜態屬性引用的物件
- 方法區常量引用的物件
- 本地方法區引用的 物件
2.2 垃圾收集演算法
標記清除
定義: 用在老年代,收集的空間是不連續的,大物件無法分配時,可能會提前觸發一次full GC.
複製
定義: 用在新生代,有一個Eden區和兩個survivor區, 按8:1的比例,每次minor gc的時候會把Eden和一個survivor區的存活物件分配到另一個survivor區中,如果survivor區的空間不夠,就直接分配到老年代
標記整理
定義: 和標記清除相比,就是整理的空間是連續的
分代收集
定義: 新生代用複製演算法,老年代用標記清除或標記整理演算法
2.3 垃圾收集器
新生代
serial
- 單執行緒
- 進行垃圾收集,必須暫停其他所有工作執行緒,"Stop The World"
- 簡單高效,Client模式下首選
parNew
1.除了使用多條執行緒進行垃圾收集之外,其他與Serial相同- 可以與CMS配合
parallel scavenger
- 吞吐量收集器
- 最大停頓時間引數(調低停頓時間,會增加停頓頻率)
- 停頓比率引數(0-100)
- GC自適應的調節策略
老年代
serial old
- 在CMS併發收集發生Concurrent Mode Failure時,作為備胎使用
cms
停頓段,併發收集
對CPU資源敏感
CPU不足4個時,影響程式的效能
無法清除浮動垃圾
浮動垃圾: 和使用者執行緒並行執行產生的新垃圾.
這一部分垃圾無法回收,因為cms和使用者執行緒行執行,所以需要留一部分記憶體給cms使用,老年代空間使用92%以上時啟動cms,如果空間不夠會報"Concurrent mode Failure",這時候會啟動備用方案serial old進行垃圾回收.
標記清除演算法
收集的空間是不完整的
parallel scavenger old
parallel scavenger的老年代版本
G1
特點:
充分利用計算機資源
分代收集
空間整合
建立可預測的停頓通過remember set解決全域性掃描region物件互相引用問題. 在reference物件進行寫操作的時候 ,發生一個寫中斷,如果有跨region引用就通過cardTable把資訊記錄到region的remember set中,在垃圾回收的時候通過列舉根節點和remember set就可以不會遺漏要回收的垃圾物件了.
回收的四個步驟:
初始標記
併發標記
最終標記
篩選回收找到所有GCRoots,修改ntmd的值,在進行併發標記的時候,保證使用者執行緒可以將物件分配在正確可用的region中
使用者執行緒和垃圾收集執行緒同時執行
同時執行產生變動的那部分,記錄到remember set logs 中,並把這個logs的內容放到 remember set中
在使用者規定的時間裡,回收最大價值的region的垃圾特點
- 充分利用計算機資源
- 分代收集
- 空間整合
- 建立可預測的停頓
垃圾收集步驟
初始標記
找到所有GCRoots,修改ntmd的值,在進行併發標記的時候,保證使用者執行緒可以將物件分配在正確可用的region中
併發標記
使用者執行緒和垃圾收集執行緒同時執行
最終標記
同時執行產生變動的那部分,記錄到remember set logs 中,並把這個logs的內容放到 remember set中
篩選回收
在使用者規定的時間裡,回收最大價值的region的垃圾
2.3.1 垃圾收集器配合使用圖
之所以進行這麼多選擇,是為了盡全力避免Full GC.
2.4 記憶體分配與回收策略
- 物件優先在eden分配
- 大物件直接進入老年代
- 長期存活的物件將進入老年代
- 動態物件年齡判定
同年齡的物件的大小超過整個Survivor區的一半,大於等於這個年齡的物件都會被放入老年代.
- 空間分配擔保流程圖
3.1 實戰
解決問題
堆溢位
報錯: java.lang.OutOfMemoryError: Java heap space
原因: 一般都是應該回收的物件沒有回收造成的記憶體溢位.
解決: 看是否有應該回收的物件沒有回收,如果沒有嘗試擴大堆記憶體
棧溢位
報錯: StackOverflowError
原因: 遞迴呼叫太多,一般不會出現這種錯誤
解決: 1. 調大棧深度 2. 從程式碼入手,改正錯誤的呼叫方法
方法區逸出
Java開始使用元空間替換永久代.
元空間不在虛擬機器中,使用的是本地記憶體.
解決元空間導致的記憶體溢位的方法:
先看是不是程式碼有錯誤,導致元空間佔用過多的記憶體
如果不是,那就只能擴充套件本機記憶體了本機直接記憶體溢位
報錯: java.lang.OutOfMemoryError: null
at sun.misc.Unsafe.allocateMemory(Native Method) ~[na:1.8.0_201]解決:
使用引數-XX:MaxDirectMemorySize=10M
調大直接記憶體大小
gc日誌分析
設定引數: -Xloggc:D:/logs/admin_client.log
需要提前建好目錄
- admin_client.log
設定虛擬機器引數
- -Xmx3550m 最大堆大小
- -Xms3550m 最小堆大小
- -Xmn2g 新生代
-Xss128k 虛擬機器棧大小
設定每個執行緒的堆疊大小。JDK5.0以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K。根據應用的執行緒所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程式內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。
設定垃圾回收器
在Java8中的測試,預設垃圾收集器Parallel GC是最快的,可以不進行更換
-XX:+HeapDumpOnOutOfMemoryError自動生成堆轉儲檔案的引數
-XX:+HeapDumpOnOutOfMemoryError
報錯: Failed to write core dump. Minidumps are not enabled by default on client versions
加入引數: -XX:+CreateMinidumpOnCrash
-Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:+CreateMinidumpOnCrash
生成的hprof檔案可以使用Jprofiler直接開啟