Java 運行時的jvm劃分
程序計數器
記錄當前線程所執行的字節碼行號,用於獲取下一條執行的字節碼。
當多線程運行時,每個線程切換後需要知道上一次所運行的狀態、位置。由此也可以看出程序計數器是每個線程私有的。
虛擬機棧
虛擬機棧由一個一個的棧幀組成,棧幀是在每一個方法調用時產生的。
每一個棧幀由
局部变量区
、操作数栈
等組成。每創建一個棧幀壓棧,當一個方法執行完畢之後則出棧。
- 如果出現方法遞歸調用出現死循環的話就會造成棧幀過多,最終會拋出
StackOverflowError
。- 若線程執行過程中棧幀大小超出虛擬機棧限制,則會拋出
StackOverflowError
。- 若虛擬機棧允許動態擴展,但在嘗試擴展時內存不足,或者在為一個新線程初始化新的虛擬機棧時申請不到足夠的內存,則會拋出
OutOfMemoryError
。
這塊內存區域也是線程私有的。
Java 堆
Java
堆是整個虛擬機所管理的最大內存區域,所有的對象創建都是在這個區域進行內存分配。
可利用參數
-Xms -Xmx
進行堆內存控制。
這塊區域也是垃圾回收器重點管理的區域,由於大多數垃圾回收器都採用
分代回收算法
,所有堆內存也分為新生代
、老年代
,可以方便垃圾的準確回收。
這塊內存屬於線程共享區域。
方法區(JDK1.7)
方法區主要用於存放已經被虛擬機加載的類信息,如
常量,静态变量
。這塊區域也被稱為永久代
。
可利用參數
-XX:PermSize -XX:MaxPermSize
控制初始化方法區和最大方法區大小。元數據區(JDK1.8)
在
JDK1.8
中已經移除了方法區(永久代),並使用了一個元數據區域進行代替(Metaspace
)。
默認情況下元數據區域會根據使用情況動態調整,避免了在1.7中由於加載類過多從而出現
java.lang.OutOfMemoryError: PermGen
。
但也不能無限擴展,因此可以使用
-XX:MaxMetaspaceSize
來控制最大內存。運行時常量池
運行時常量池是方法區的一部分,其中存放了一些符號引用。當
new
一個對象時,會檢查這個區域是否有這個符號的引用。直接內存
直接內存又稱為
Direct Memory(堆外内存)
,它並不是由JVM
虛擬機所管理的一塊內存區域。
有使用過
Netty
的朋友應該對這塊並內存不陌生,在Netty
中所有的IO(nio)操作都會通過Native
函數直接分配堆外內存。
它是通過在堆內存中的
DirectByteBuffer
對像操作的堆外內存,避免了堆內存和堆外內存來回複製交換複製,這樣的高效操作也稱為零拷贝
。
既然是內存,那也得是可以被回收的。但由於堆外內存不直接受
JVM
管理,所以常規GC
操作並不能回收堆外內存。它是藉助於老年代產生的fullGC
順便進行回收。同時也可以顯式調用System.gc()
方法進行回收(前提是沒有使用-XX:+DisableExplicitGC
參數來禁止該方法)。
值得注意的是:由於堆外內存也是內存,是由操作系統管理。如果應用有使用堆外內存則需要平衡虛擬機的堆內存和堆外內存的使用佔比。避免出現堆外內存溢出。
常用參數
通過上圖可以直觀的查看各個區域的參數設置。
常見的如下:
-Xms64m
最小堆內存64m
.-Xmx128m
最大堆內存128m
.-XX:NewSize=30m
新生代初始化大小為30m
.-XX:MaxNewSize=40m
新生代最大大小為40m
.-Xss=256k
線程棧大小。-XX:+PrintHeapAtGC
當發生GC 時打印內存佈局。-XX:+HeapDumpOnOutOfMemoryError
發送內存溢出時dump 內存。
新生代和老年代的默認比例為
1:2
,也就是說新生代佔用1/3
的堆內存,而老年代佔用2/3
的堆內存。
可以通過參數
-XX:NewRatio=2
來設置老年代/新生代的比例。
留言
張貼留言