6.堆
6.1 堆的特点
使用new关键字创建的对象都会使用堆。
特点:
- 线程共享,堆中的对象需要考虑线程安全问题。
- 具有垃圾回收机制。
6.2 堆内存溢出问题
堆中具有垃圾回收机制,但是垃圾回收的前提是堆中的对象不再被引用(实际上,回收引用的算法是根可达算法,后面会讲述,这里的表述是不准确的),因此如果我们有过多无法被回收的对象,就可能导致内存溢出。
public class MemoryOverFlow { public static void main(String[] args) { int i = 0; String a = "hello"; List list = new ArrayList(); // 直到catch代码块执行,一直被使用 try { while (true) { list.add(a); a = a + a; i++; } } catch (Throwable e) { //使用Throwable,如果使用Exception包不住Error,i无法被打印出来 e.printStackTrace(); System.out.println(i); } } }
出现OutOfMemoryError
java.lang.OutOfMemoryError: Overflow: String length out of range at java.base/java.lang.StringConcatHelper.checkOverflow(StringConcatHelper.java:57) at java.base/java.lang.StringConcatHelper.mix(StringConcatHelper.java:138) at java.base/java.lang.StringConcatHelper.simpleConcat(StringConcatHelper.java:420) at MemoryOverFlow.main(MemoryOverFlow.java:12) 28
另:参数-Xmx 可以设置jvm内存空间大小,排查堆内存问题时可以将其设置得比较小(如8m),更容易暴露出内存溢出问题。设置方法:点击build and run同行的modify options->add vm options.
6.3 代码内存性能影响的评估
工作中编写了一段代码,如何去判断一段代码对于内存性能的影响呢?可以借助如下工具。
jps 查看系统有哪些java进程
jmap 查看某一时刻堆内存的占用情况
jconsole 多功能实时监测工具
通过下面的demo来演示。
public class jvmdemo { public static void main(String[] args) throws InterruptedException { System.out.println("1....."); //输出提示,方便进行Heap Dump Thread.sleep(60000); //给30s时间用于Heap Dump byte [] arr = new byte[1024 * 1024 * 10]; System.out.println("2......."); Thread.sleep(60000); arr = null; System.gc(); System.out.println("3......"); Thread.sleep(100000L); } }
当输出1…后,先执行jps查看jvmdemo对应的pid
💡 tip:如果您是windows系统,jps无返回结果,可以参考博客
Windows中jps命令无法查看java进程问题_无数_mirage的博客-CSDN博客_windows 查看java进程
结果如下。
执行jmap -heap xxx(pid)查看此时堆内存占用情况。
💡 tip:
如果执行jmap报错。
Error: -heap option used Cannot connect to core dump or remote debug server. Use jhsdb jmap instead
是因为jdk8之后的版本之前的jmap -heap xxx(pid)命令不可再使用。可以改用命令jhsdb jmap --heap --pid xxx.
在提示信息输出1,2,3后分别进行三次操作得到的结果如下。
Heap Usage: G1 Heap: regions = 2034 capacity = 8531214336 (8136.0MB) used = 0 (0.0MB) free = 8531214336 (8136.0MB) 0.0% used G1 Young Generation: Eden Space: regions = 0 capacity = 29360128 (28.0MB) used = 0 (0.0MB) free = 29360128 (28.0MB) 0.0% used Survivor Space: regions = 0 capacity = 0 (0.0MB) used = 0 (0.0MB) free = 0 (0.0MB) 0.0% used G1 Old Generation: regions = 0 capacity = 507510784 (484.0MB) used = 0 (0.0MB) free = 507510784 (484.0MB) 0.0% used
Heap Usage: G1 Heap: regions = 2034 capacity = 8531214336 (8136.0MB) used = 12582912 (12.0MB) free = 8518631424 (8124.0MB) 0.14749262536873156% used G1 Young Generation: Eden Space: regions = 0 capacity = 29360128 (28.0MB) used = 0 (0.0MB) free = 29360128 (28.0MB) 0.0% used Survivor Space: regions = 0 capacity = 0 (0.0MB) used = 0 (0.0MB) free = 0 (0.0MB) 0.0% used G1 Old Generation: regions = 3 capacity = 507510784 (484.0MB) used = 12582912 (12.0MB) free = 494927872 (472.0MB) 2.479338842975207% used
Heap Usage: G1 Heap: regions = 2034 capacity = 8531214336 (8136.0MB) used = 673872 (0.6426544189453125MB) free = 8530540464 (8135.357345581055MB) 0.007898898954588403% used G1 Young Generation: Eden Space: regions = 0 capacity = 8388608 (8.0MB) used = 0 (0.0MB) free = 8388608 (8.0MB) 0.0% used Survivor Space: regions = 0 capacity = 0 (0.0MB) used = 0 (0.0MB) free = 0 (0.0MB) 0.0% used G1 Old Generation: regions = 1 capacity = 8388608 (8.0MB) used = 673872 (0.6426544189453125MB) free = 7714736 (7.3573455810546875MB) 8.033180236816406% used
重点查看uesd这一项,可以看到代码中内存的变化过程,这里JVM version 是16.0.2+7-67,不同版本可能略有差异。
使用jconsole可以实时观测数据,而且不仅仅可以观测本地进程,还可以观测远程进程。