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 />![image.png](https://cdn.nlark.com/yuque/0/2020/png/285619/1586439726983-b6aaafeb-b9a5-4fe6-a883-5d317428dad7.png#align=left&display=inline&height=204&margin=%5Bobject%20Object%5D&name=image.png&originHeight=402&originWidth=1468&size=84950&status=done&style=shadow&width=746)<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)
```java
import 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方法,要在线程休眠结束之后才会被释放。在分析堆内存的时候要看对象的作用范围。