物件建立與堆
阿新 • • 發佈:2020-06-23
這一節主要介紹物件建立時,在堆中的一些過程。
回憶下,我們之前說的,什麼時候會發生垃圾回收?
除了在一些安全點處也許會發生垃圾回收(只是也許),如果在所需記憶體不足的情況下,一定會發生垃圾回收。
### 分配堆空間
首先通過設定引數,把堆空間設定為 20M,其中 新生代 10M,老年代 10M。
引數設定:
`-Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails`
結果為:
```
Heap
PSYoungGen total 9216K, used 1685K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 20% used [0x00000007bf600000,0x00000007bf7a5580,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
ParOldGen total 10240K, used 0K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 0% used [0x00000007bec00000,0x00000007bec00000,0x00000007bf600000)
```
### 建立一個新物件
我們首先建立一個物件,這個物件佔用 2M 的空間。
```java
package heap;
public class CreateObject {
public static void main(String[] args) {
byte[] obj1 = new byte[1024 * 1024 * 2];
}
}
```
最後的輸出:
```java
Heap
PSYoungGen total 9216K, used 3733K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 45% used [0x00000007bf600000,0x00000007bf9a5590,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
ParOldGen total 10240K, used 0K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 0% used [0x00000007bec00000,0x00000007bec00000,0x00000007bf600000)
```
可以看到,新生代 被佔用了,老年代佔用為 0K,沒有被使用。
**所以,new 的物件先放在 eden 區。**
### 填滿 eden 區
在填滿 eden 區後,會發生什麼呢?因為 survivor 區實在太小了,很難看到。所以,這裡可以藉助 Visual VM,來觀察,更加直觀。
程式如下:
```java
package heap;
public class CreateObject {
public static void main(String[] args) {
while(true){
byte[] bytes = new byte[1024 * 512];
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
```
![](https://pic.imgdb.cn/item/5ef088c114195aa594c94b03.png)
重點是看右邊的記錄圖。注意,這裡我們將每次建立物件的大小設定為了 0.5M。
當 Eden 滿的時候,會呼叫垃圾回收器,呼叫垃圾回收器後,Eden 出現了低谷,Survivor 出現了一個增長。老年區也出現了一個增長。
**當 Eden 滿的時候,如果 Survivor 區有足夠的空間容納存活物件,那麼可以把存活物件放入 Survivor,多的物件放入老年區。**
現在,我們把物件的大小調大。設定為 2M,這樣 Survivor 就無法存放下。
![](https://pic.imgdb.cn/item/5ef0952e14195aa594da6096.png)
可以看到,在經過一次垃圾回收的時候(可以看到GC Time 上有波峰,說明執行了一次垃圾回收),但我們注意到,Survivor 區中並沒有被佔用。說明垃圾回收過程中,直接將存活物件放到了老年代中。
### 再來聊聊 survivor 區
> 物件通常在 Eden 區裡誕生,如果經過第一次 MInor GC 後仍然存活,並且能夠被 Survivor 容納的話,該物件會被移動到 Survivor 區,並且將其年齡設定為 1 歲。物件在 Survivor 區每熬過一次 Minor GC,年齡就增加 1 歲,當它年齡增大到一定程度(預設是 15 歲),就會被晉升到老年代。
### 特殊情況
有些時候,如果使用者建立了大物件,如很長的字串或者元素很多的陣列的時候。這種大物件都佔用大量的記憶體,像這種大物件,有很大概率是長時間使用的,不然為什麼要建立大物件。
如果大物件朝生夕滅,我們知道在 Java 8 中,新生代預設採用的 標記-複製 演算法,那麼對於大物件而言,是非常耗時的。
所以,如果 JVM 設定了一個閾值,那麼當分配的物件大於這個閾值的時候,會直接被分配到老年代。
### 總結
![](https://pic.imgdb.cn/item/5ef0d02f14195aa5941251