Heap 堆java
經過new關鍵字,建立對象都會使用堆內存數組
線程共享的,堆中對象都須要考慮線程安全的問題安全
有垃圾回收機制app
Java 堆(Java Heap)是 Java 虛擬機所管理的內存中最大的一塊,也被稱爲 「GC堆」,是被全部線程共享的一塊內存區域,在虛擬機啓動時被建立。工具
惟一目的就是儲存對象實例和數組(JDK7 已把字符串常量池和類靜態變量移動到 Java 堆),幾乎全部的對象實例都會存儲在堆中分配。隨着 JIT 編譯器發展,逃逸分析、棧上分配、標量替換等優化技術致使並非全部對象都會在堆上分配。優化
Java 堆是垃圾收集器管理的主要區域。堆內存分爲新生代 (Young) 和老年代 (Old) ,新生代 (Young) 又被劃分爲三個區域:Eden、From Survivor、To Survivor。ui
若是 Java 堆嘗試擴展內存的時候沒法申請到足夠的內存,那 Java 虛擬機將拋出一個 OutOfMemoryError 異常。spa
問題:既然有垃圾回收機制,爲什麼還會出現堆內存溢出的狀況呢?線程
解答:垃圾回收機制是回收不被使用的對象,當對象被引用或間接引用時,就不會被垃圾回收,致使內存佔用愈來愈大,從而出現內存溢出。code
示例代碼
/** * -Xmx8m 調整堆內存最大爲8m */ public class Demo { public static void main(String[] args) { int i = 0; try { List<Object> list = new ArrayList<>(); String a = "hello"; while (true) { list.add(a); a = a + a; i++; } } catch (Throwable e) { e.printStackTrace(); System.out.println(i); } } }
輸出
java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448) at java.lang.StringBuilder.append(StringBuilder.java:136) at com.loksail.learndemo.Demo.main(Demo.java:18) 17
經過調整最大堆內存爲8m,list
在添加到第18個元素時,就出現了內存溢出,主要緣由是程序中list
集合一直處於使用中,且不斷建立的String
對象一樣被list
集合引用,從而沒法被垃圾回收,致使list
集合所佔內存愈來愈大及String
對象愈來愈多,從而拋出OutOfMemoryError
異常。
查看當前系統中有哪些java進程
[root@iZwz9d10hr1juhw84f6r31Z ~]# jps 1237 AgentDaemon 27773 es-yxfbp-main-1.0.0.jar 2974 Jps
查看當前堆內存佔用狀況
jmap -heap 進程id
示例代碼
public class Demo { public static void main(String[] args) throws InterruptedException { System.out.println("1..........."); Thread.sleep(20000L); byte[] bytes = new byte[1024 * 1024 * 10]; //10m System.out.println("2..........."); Thread.sleep(10000L); bytes = null; System.gc(); System.out.println("3..........."); Thread.sleep(100000000L); } }
查看當前進程號
D:\JWF\Gitee\learn-demo\src\main\java\com\loksail\learndemo>jps 15380 Demo 12840 Launcher 17000 Jps 3928 10012 Launcher
當打印1………..後的內存輸出
D:\JWF\Gitee\learn-demo\src\main\java\com\loksail\learndemo>jmap -heap 15380 Attaching to process ID 15380, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.221-b11 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 4261412864 (4064.0MB) NewSize = 88604672 (84.5MB) MaxNewSize = 1420296192 (1354.5MB) OldSize = 177733632 (169.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 66584576 (63.5MB) used = 6658584 (6.350120544433594MB) free = 59925992 (57.149879455566406MB) 10.00018983375369% used From Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used To Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used PS Old Generation capacity = 177733632 (169.5MB) used = 0 (0.0MB) free = 177733632 (169.5MB) 0.0% used 3170 interned Strings occupying 260168 bytes.
當打印2………..後的內存輸出
D:\JWF\Gitee\learn-demo\src\main\java\com\loksail\learndemo>jmap -heap 15380 Attaching to process ID 15380, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.221-b11 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 4261412864 (4064.0MB) NewSize = 88604672 (84.5MB) MaxNewSize = 1420296192 (1354.5MB) OldSize = 177733632 (169.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 66584576 (63.5MB) used = 17144360 (16.350135803222656MB) free = 49440216 (47.149864196777344MB) 25.748245359405757% used From Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used To Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used PS Old Generation capacity = 177733632 (169.5MB) used = 0 (0.0MB) free = 177733632 (169.5MB) 0.0% used 3171 interned Strings occupying 260232 bytes.
當打印3………..後的內存輸出
D:\JWF\Gitee\learn-demo\src\main\java\com\loksail\learndemo>jmap -heap 15380 Attaching to process ID 15380, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.221-b11 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 4261412864 (4064.0MB) NewSize = 88604672 (84.5MB) MaxNewSize = 1420296192 (1354.5MB) OldSize = 177733632 (169.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 66584576 (63.5MB) used = 1331712 (1.27001953125MB) free = 65252864 (62.22998046875MB) 2.0000307578740157% used From Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used To Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used PS Old Generation capacity = 177733632 (169.5MB) used = 1083488 (1.033294677734375MB) free = 176650144 (168.46670532226562MB) 0.6096133791943216% used 3157 interned Strings occupying 259256 bytes.
分析
在輸出的內存信息中,Heap Configuration
爲程序的堆設置,Heap Usage爲堆使用狀況,咱們最主要關注的是Eden Space的使用狀況
在打印1以後,使用的堆內存爲6.350120544433594MB,此時並無對象建立
在打印2以後,使用的堆內存爲16.350135803222656MB,多增長10M左右是由於代碼中建立了10M的byte數組
在打印3以後,使用的堆內存爲1.27001953125MB,由於此時byte數組置爲空,對應的對象空間再也不使用,此時調用垃圾回收將會回收這部份內存,從而使堆內存的使用降低。
生成堆內存轉儲快照
jmap -dump:format=b,file=文件名稱 進程號 如:jmap -dump:format=b,file=D://demo.hprof 13777
將堆內存dump下來後,使用jvisualvm
加載文件,一樣能夠分析堆內存佔用狀況
jconsole
執行以前的Demo程序,經過jconsole
能夠明顯觀察到堆內存的變化,同時jconsole
能夠觀察線程及CPU
的相關信息,也可手動進行垃圾回收。
jvisualvm
此工具與jconsole功能類似,可是其可經過堆dump來查看堆內存的具體佔用狀況
示例代碼
public class Demo { public static void main(String[] args) throws InterruptedException { List<Student> students = new ArrayList<>(); for (int i = 0; i < 200; i++) { students.add(new Student()); } Thread.sleep(100000000); } static class Student { private byte[] bytes = new byte[1024 * 1024];; } }
經過jvisualvm
可看到當前堆內存信息,堆內存佔用229m
左右
點擊執行垃圾回收後,堆內存佔用218m
左右
能夠看到堆內存佔用並無減小多少,若是不分析代碼,如何來分析堆內存的具體佔用信息呢?
使用jvisualvm
的堆dump
經過右側的查看大小最大的對象能夠看到,ArrayList
對象佔用堆內存最大,點擊查看
能夠看到,ArrayList
對象佔用了200m
左右的內存,其size爲200
,集合中元素爲Student
對象,大小爲1m
,主要是byte
數組佔用了1m
。也就是存儲了200
個Student
對象的list
集合佔用了200m
的堆內存,再結合代碼分析可知,因爲ArrayList
對象仍在方法的做用域中,仍被虛擬機棧的局部變量表引用,從而不會被垃圾回收,從而致使堆內存佔用不會降低。
歡迎關注公衆號,後續文章更新通知,一塊兒討論技術問題 。