傳聞:《半條命:Alyx》將登陸PSVR 2
什麼是JVM
以下所寫JVM內容都是基於hotspot。
定義:Java Virtual Machine - java程式的執行環境(Java 二進位制位元組碼的執行環境)。
好處:
- 一次編寫,到處執行。(遮蔽了作業系統底層的差異)
- 自動記憶體管理,垃圾回收功能。
- 陣列下標越界檢查
- 多型
比較:
- JVM <————> 作業系統(Windows,MacOS,Linux。。。)
- JRE(JVM + 核心類庫)
- JDK(JVM+核心類庫+編譯工具)
- JAVA SE(JDK + IDE工具)
- JAVA EE (JDK+應用伺服器+IDE工具)
JVM指令->直譯器->機器碼->CPU
直譯器 ->程式計數器拿到下一條指令地址
JVM 記憶體結構
- 虛擬機器棧
- 堆
- 方法區
- 本地方法棧
- 程式計數器
1、程式計數器(CPU中的暫存器實現)
定義:Program Counter Register 程式計數器(暫存器)
作用:記住下一條JVM指令執行的地址
特點:
- 執行緒私有的
- 不會存在記憶體溢位
程式計數器:是jvm記憶體區域中一塊較小的記憶體區域,其中記錄的是當前執行緒執行到的位元組碼的行號,位元組碼直譯器工作時通過改變程式計數器的值來選取下一條需要執行的位元組碼指令。
線上程切換過程中,程式計數器記錄當前執行緒執行的位元組碼指令行號,再切換回該執行緒時,能保證正確執行。所以程式計數器是執行緒私有的。
注意:程式計數器是唯一一個不會出現 OutOfMemoryError
的記憶體區域,它的生命週期隨著執行緒的建立而建立,隨著執行緒的結束而死亡。
2、虛擬機器棧
棧:執行緒執行需要的記憶體空間
棧幀:每個方法執行時需要的記憶體(引數、區域性變數、返回地址...)
定義:Java Virtual Machine Stacks(Java虛擬機器棧)
- 每個執行緒執行時所需要的記憶體,稱為虛擬機器棧
- 每個棧由多個棧幀(Frame)組成,對應著每次方法呼叫時所佔用的記憶體
- 每個執行緒只能有一個活動棧幀,對應著當前正在執行的那個方法
垃圾回收不需要對棧記憶體進行處理,棧幀出棧自動釋放。
指定棧記憶體大小 虛擬機器引數 -Xss size
棧記憶體並不是越大越好,棧記憶體越大,執行緒數目越少。
區域性變數是執行緒私有的,若不逃離方法的作用範圍(作為其他方法入參/作為反參),不會有安全問題。
-
棧記憶體溢位(Java.lang.StackOverflowError)
- 棧幀過多導致棧記憶體溢位,常見於遞迴呼叫方法。
- 棧幀過大導致棧記憶體溢位,不太容易出現。
-
執行緒執行診斷
-
CPU佔用過多:
- Linux下top命令定位哪個程序對cpu的佔用過高
- ps進一步定位哪個執行緒引起的cpu佔用過高 ps H -eo pid,tid,%cpu | grep 程序號
- jstack 程序ID 展示的nid(native thread id)是16進位制,要將10進位制轉成16進位制,可以根據執行緒ID找到有問題的執行緒,進一步定位到問題程式碼的原始碼行數。
-
程式執行很長時間沒有結果:
jstack 排查 (死鎖)
-
3、本地方法棧
作用:給本地方法的執行提供執行的記憶體空間
native method 不是由java程式碼編寫的方法,如不能直接與作業系統底層互動。
Java關鍵字 native,如Object類裡的public native int hashCode();
4、堆
Heap 堆:通過new關鍵字建立的物件都會使用堆記憶體
特點:
- 它是執行緒共享的,堆中物件都需要考慮執行緒安全的問題
- 有垃圾回收機制
堆記憶體溢位(java.lang.OutOfMemoryError: Java heap space)
指定堆記憶體大小 虛擬機器引數 -Xmx size
堆記憶體診斷
-
jps工具
檢視當前系統中有哪些java程序
-
jmap工具
jmap -heap 程序id,檢視某一時刻堆記憶體佔用情況
-
jconsole工具,jvisualvm(堆dump)
圖形介面的,多功能的檢測工具,可以連續監測
5、方法區
方法區是規範,永久代(JDK8以前)或元空間是實現,用於儲存類的資料
方法去記憶體溢位:
- 1.8以前會導致永久代記憶體溢位(java.lang.OutOfMemoryError: PermGen space)
- 1.8之後會導致元空間記憶體溢位(java.lang.OutOfMemoryError: Metaspace)
指定永久代記憶體大小 虛擬機器引數 -XX:MaxPermSize=size
元空間預設使用系統記憶體,沒有設定上限
指定元空間記憶體大小 虛擬機器引數 -XX:MaxMetaspaceSize=size
執行時常量池
javap -v xxx.class 反編譯class檔案的詳細資訊
二進位制位元組碼(類基本資訊,常量池,類方法定義,包含了虛擬機器指令)
- 常量池,就是一張表,虛擬機器指令根據這張常量表找到要執行的類名、方法名、引數型別、字面量等資訊
- 執行時常量池,常量池是*.class檔案中的,當該類被載入,它的常量池資訊就會放入執行時常量池,並把裡面的符號地址變為真實地址
StringTable(俗稱:串池)
資料結構上是hashtable結構,不能擴容,取值唯一。
特性:
- 常量池中的字串僅是符號,第一次用到時才變成物件
- 利用串池的機制,來避免重複建立字串物件
- 字串變數拼接的原理是StringBuilder(1.8)
- 字串常量拼接的原理是編譯期間優化
- 可以使用Intern方法,主動將串池中還沒有的字串物件放入串池。
- 1.8 將這個字串物件嘗試放入串池,如果有則不會放入串池,如果沒有則放入串池,會把串池中的物件返回
- 1.6 將這個字串物件嘗試放入串池,如果有則不會放入串池,如果沒有則會把此物件複製一份放入串池,會把串池中的物件返回
位置:
1.6:永久代 ,垃圾回收需要full GC
1.8:堆,垃圾回收minor GC即可
效能調優:
指定StringTable大小 虛擬機器引數 -XX:StringTableSize=桶個數 buckets 桶數調大減少hash衝突
考慮將字串物件是否入池
6、直接記憶體
作業系統的記憶體
Direct Memory
- 常見於NIO操作時,用於資料緩衝區
- 分配回收成本較高,但讀寫效能高
- 不受JVM記憶體回收管理
Java裡UnSafe物件可以分配(allocateMemory,setMemory方法)、釋放(freeMemory方法)直接記憶體。Java垃圾回收只限於Java記憶體
分配和回收原理:
- 使用了Unsafe物件完成直接記憶體的分配回收,並且回收需要主動呼叫freeMemory方法
- ByteBuffer的實現類內部,使用了Cleaner(虛引用)來檢測ByteBuffer物件,一旦ByteBuffer物件被垃圾回收,那麼就會由ReferenceHandler執行緒通過Cleaner的clean方法呼叫freeMemory來釋放直接記憶體
System.gc()是Full GC 虛擬機器引數 -XX:+DisableExplicitGC 可以禁用顯示的垃圾回收