我們知道java虛擬機在運行時會把它管理的內存分爲若干個區域,有的隨着虛擬機的啓動而啓動,有的隨着應用線程的啓動和創建,那下面我們就看看虛擬機運行時各個分區及其作用。
其實JVM對運行時內存進行分區的主要目的就是爲了更好的進行垃圾回收和數據管理。
這張圖截自 周志明 的《深入理解java虛擬機》一書。
(1)方法區:
方法區是各個線程共享的區域,它用於存儲已經被JVM加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。
方法區有時也被稱爲“永久代”,當然這只是JVM垃圾回收策略的劃分方法。
實際上方法區很少進行垃圾回收,jdk1.7之前的垃圾回收基本上只是對常量池回收和java已加載類的類型卸載。
方法區可以通過PermSize和MaxPermSize參數進行設置大小如: -XX:PermSize=5M -XX:MaxPermSize=7M
說明: PermSize爲永久區大小, MaxPermSize爲最大的永久區大小。
/**
* 常量池位於方法區中,我們通過不停往常量區中添加常量來填充方法區內存
* @author yujie.wang3
* -XX:PermSize=5M -XX:MaxPermSize=5M -XX:+HeapDumpOnOutOfMemoryError
*/
public class MthodOutOfMemoryErrorTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> list = new ArrayList<String>();
int i = 0;
while(true){
list.add(String.valueOf(i++).intern());
}
}
}
當常量池溢出則拋出OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid7176.hprof ...
Heap dump file created [10644158 bytes in 0.168 secs]
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at com.renren.yujie.c2.MthodOutOfMemoryErrorTest.main(MthodOutOfMemoryErrorTest.java:17)
(2)堆區
同方法區一樣,堆區也是被所有應用程序共享的一塊內存區域,在虛擬機啓動時創建。這塊區域的目的主要是存放對象和數組數據。同時堆區也是JVM所管理的最大的一塊內存,垃圾收集器主要管理的區域也是堆區,因此很多時候堆區也被稱爲“GC堆”,
如果採用分代垃圾回收策略,那麼java堆還可以細分爲:新生代和老年代;在細緻一點有Eden空間,From Survivor空間,
To Survivor空間等。從內存分配的角度來看,java堆中可以劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer, TLAB)。
堆區可以是物理上連續的內存空間,也可以是不連續的內存空間。
堆區可以通過Xms和Xmx參數進行設置大小如: -Xms500M -Xmx500M
說明: Xms爲初始堆大小, Xmx爲最大堆大小。
/**
* @author yujie.wang3
* 通過在堆區創建大量對象來佔用堆區內存
* 用於演示java 堆內存的溢出
* -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
*/
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
static class OOMObject{
}
public static void main(String[] args){
List<OOMObject> list = new ArrayList<OOMObject>();
while(true){
list.add(new OOMObject());
}
}
}
拋出內存溢出異常:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid13156.hprof ...
Heap dump file created [15802426 bytes in 0.099 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at com.renren.yujie.c2.HeapOOM.main(HeapOOM.java:19)
(3)虛擬機棧和本地方法棧
虛擬機棧是線程私有的一塊內存,它的生命週期和線程的週末週期相同,虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行時都會創建一個棧幀,用於存儲局部變量表,操作數棧,動態連接,方法出口信息,每一個方法從調用到完成都對應着入棧和出棧的過程。
虛擬機棧可以使用Xss參數進行設置大小如:-Xss256K
本地方法棧和虛擬機棧的功能是類似的,只不過本地方法棧是爲虛擬機使用到的Native方法服務,有一些虛擬機直接將虛擬機棧和本地方法棧合二爲一。
/**
* 驗證本地方法棧OOM SOF
* 通過不設深度的遞歸調用 將拋出 StackOverflowError
* @author yujie.wang3
* -Xss128k -XX:+HeapDumpOnOutOfMemoryError
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Exception e) {
// TODO: handle exception
System.out.println("lentho"+oom.stackLength);
}
}
}
如果請求的棧深度大約虛擬機所允許的最大深度,將拋出StackOverflowError
Exception in thread "main" java.lang.StackOverflowError
at com.renren.yujie.c2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
(4)程序計數器
程序計數器、虛擬機棧和本地方法棧都是線程私有的內存區域,這是一塊很小的內存空間,用於存儲線程的上下文。我們知道併發程序的執行是通過使用CPU的時間片來執行,當線程繼續切換時,就回將當前線程執行的一些信息存儲到程序計數器這塊內存中,以便之後恢復執行。
這個區域是java虛擬機規範中唯一一個沒有規定任何OutOfMemoryError的區域