1. 程式人生 > >物件建立與堆

物件建立與堆

這一節主要介紹物件建立時,在堆中的一些過程。 回憶下,我們之前說的,什麼時候會發生垃圾回收? 除了在一些安全點處也許會發生垃圾回收(只是也許),如果在所需記憶體不足的情況下,一定會發生垃圾回收。 ### 分配堆空間 首先通過設定引數,把堆空間設定為 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