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
对象仍在方法的做用域中,仍被虚拟机栈的局部变量表引用,从而不会被垃圾回收,从而致使堆内存占用不会降低。
欢迎关注公众号,后续文章更新通知,一块儿讨论技术问题 。