4. 堆
4.1. 定义
Heap堆
- 通过new关键词,创建对象都会使用堆内存
特点:
- 它是线程共享的,堆中对象都需要考虑线程安全的问题
- 有垃圾回收机制
4.2. 堆内存溢出
在定义中说到创建出来的对象不被使用,就会被GC回收,那这样怎么还能产生堆内存溢出呢?那么,可以这样想,对象被当成垃圾被回收的条件是没有被使用的时候,如果创建的很多对象又被使用到,就会造成堆内存溢出。4.2.1. 示例
测试程序: ```java import java.util.ArrayList;
/**
- 演示堆内存溢出
- -Xmx8m
- @author : gnehcgnaw
@since : 2020/4/9 21:36 */ public class Demo9 { public static void main(String[] args) {
int i = 0 ;ArrayList arrayList = new ArrayList();String a = "hello" ;while (true){arrayList.add(a);a = a + a ;i++ ;}
}
}
**运行结果:**<br /><br />**现实项目中出现堆内存溢出的情况,在排查的时候将xmx设置小一点, 可以更好的展示错误。**<a name="nFX12"></a>## 4.3. 堆内存诊断<a name="1rCAq"></a>### 4.3.1. 诊断工具1. jps工具- 查看当前系统中有哪些Java进程2. jmap工具- 查看堆内存占用情况(只能查看某一时刻)3. jconsole工具- 图像化界面的,多功能的检测工具,可以连续检测<a name="s6oW7"></a>### 4.3.2. 查看堆内存的占用情况(案例1)```javaimport java.util.concurrent.TimeUnit;/*** @author : <a href="mailto:gnehcgnaw@gmail.com">gnehcgnaw</a>* @since : 2020/4/9 21:45*/public class Demo10 {public static void main(String[] args) throws InterruptedException {System.out.println("1....");TimeUnit.MILLISECONDS.sleep(30000);byte [] bytes = new byte[1024*1024*10] ;System.out.println("2....");TimeUnit.MILLISECONDS.sleep(30000);bytes = null ;System.gc();System.out.println("3....");TimeUnit.MILLISECONDS.sleep(1000000L);}}
4.3.2.1. 使用jmap
我使用的是Mac,出现了一下错误:
官方说明:
- https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8160376
- https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8161164
解决:有的地方说是系统的安全权限有问题,有的地方说是 JDK 的 bug。看上诉官方说明好像是说可以使用 JDK 9。但是官方又建议使用 JDK 10:
最后更换成为 JDK 11。问题解决。
注意:如果可以使用,这需要运行三次jmap -heap pid,执行时机是三个数字的输出,原因是jmap只能查看某一时刻的堆内存占用情况。
4.4.2.2. 使用arthas
4.3.2.3. 使用jconsole
4.3.3. 垃圾回收后,内存占用仍然很高(案例2)
使用的工具是Java VisualVM
- 点击执行GC

执行完GC之后发现堆内存变化不大。
- 点击堆Dump,显示如下:

然后点击右边的查找,显示如下:(发现占用堆内存最多的是ArrayList)
- 点开集合,发现里面有一个Student对象,一个对象占用1M,总共用200个,如下所示:

代码如下所示:
import java.util.ArrayList;import java.util.concurrent.TimeUnit;/*** 案例:垃圾回收后,内存占用仍然很高,如果查找问题* @author : <a href="mailto:gnehcgnaw@gmail.com">gnehcgnaw</a>* @since : 2020/4/9 22:52*/public class Demo11 {public static void main(String[] args) throws InterruptedException {ArrayList arrayList = new ArrayList();for (int i = 0; i < 200 ; i++) {arrayList.add(new Student());}TimeUnit.MINUTES.sleep(5);}}class Student{private byte[] bytes = new byte[1024*1024];}
分析:
- Arraylist集合的作用范围是整个main方法,要在线程休眠结束之后才会被释放。在分析堆内存的时候要看对象的作用范围。
