4. 堆

image.png

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) {

    1. int i = 0 ;
    2. ArrayList arrayList = new ArrayList();
    3. String a = "hello" ;
    4. while (true){
    5. arrayList.add(a);
    6. a = a + a ;
    7. i++ ;
    8. }
  1. }

}

  1. **运行结果:**<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设置小一点, 可以更好的展示错误。**
  2. <a name="nFX12"></a>
  3. ## 4.3. 堆内存诊断
  4. <a name="1rCAq"></a>
  5. ### 4.3.1. 诊断工具
  6. 1. jps工具
  7. - 查看当前系统中有哪些Java进程
  8. 2. jmap工具
  9. - 查看堆内存占用情况(只能查看某一时刻)
  10. 3. jconsole工具
  11. - 图像化界面的,多功能的检测工具,可以连续检测
  12. <a name="s6oW7"></a>
  13. ### 4.3.2. 查看堆内存的占用情况(案例1)
  14. ```java
  15. import java.util.concurrent.TimeUnit;
  16. /**
  17. * @author : <a href="mailto:gnehcgnaw@gmail.com">gnehcgnaw</a>
  18. * @since : 2020/4/9 21:45
  19. */
  20. public class Demo10 {
  21. public static void main(String[] args) throws InterruptedException {
  22. System.out.println("1....");
  23. TimeUnit.MILLISECONDS.sleep(30000);
  24. byte [] bytes = new byte[1024*1024*10] ;
  25. System.out.println("2....");
  26. TimeUnit.MILLISECONDS.sleep(30000);
  27. bytes = null ;
  28. System.gc();
  29. System.out.println("3....");
  30. TimeUnit.MILLISECONDS.sleep(1000000L);
  31. }
  32. }

4.3.2.1. 使用jmap

我使用的是Mac,出现了一下错误:
image.png
官方说明:

解决:有的地方说是系统的安全权限有问题,有的地方说是 JDK 的 bug。看上诉官方说明好像是说可以使用 JDK 9。但是官方又建议使用 JDK 10:

最后更换成为 JDK 11。问题解决。
注意:如果可以使用,这需要运行三次jmap -heap pid,执行时机是三个数字的输出,原因是jmap只能查看某一时刻的堆内存占用情况。

4.4.2.2. 使用arthas

使用dashboard命令可以常看到堆的占用情况:
image.png

4.3.2.3. 使用jconsole

命令行使用jconsole命令启动窗口:
image.png

4.3.3. 垃圾回收后,内存占用仍然很高(案例2)

使用的工具是Java VisualVM

  1. 点击执行GC

image.png
执行完GC之后发现堆内存变化不大。

  1. 点击堆Dump,显示如下:

image.png
然后点击右边的查找,显示如下:(发现占用堆内存最多的是ArrayList)
image.png

  1. 点开集合,发现里面有一个Student对象,一个对象占用1M,总共用200个,如下所示:

image.png
代码如下所示:

  1. import java.util.ArrayList;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 案例:垃圾回收后,内存占用仍然很高,如果查找问题
  5. * @author : <a href="mailto:gnehcgnaw@gmail.com">gnehcgnaw</a>
  6. * @since : 2020/4/9 22:52
  7. */
  8. public class Demo11 {
  9. public static void main(String[] args) throws InterruptedException {
  10. ArrayList arrayList = new ArrayList();
  11. for (int i = 0; i < 200 ; i++) {
  12. arrayList.add(new Student());
  13. }
  14. TimeUnit.MINUTES.sleep(5);
  15. }
  16. }
  17. class Student{
  18. private byte[] bytes = new byte[1024*1024];
  19. }

分析:

  • Arraylist集合的作用范围是整个main方法,要在线程休眠结束之后才会被释放。在分析堆内存的时候要看对象的作用范围。