1.美图
2.概述
3.内存泄漏demo
准备模拟内存泄漏demo
- 定义静态变量HashMap
- 分段循环创建对象,并加入HashMap
package com.java.book.jvm.chapter4;
import java.util.HashMap;
import java.util.Map;
public class CyclicDependencies {
//声明缓存对象
private static final Map map = new HashMap();
public static void main(String args[]){
try {
//给打开visualvm时间
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//循环添加对象到缓存
for(int i=0; i<1000000;i++){
Person t = new Person();
map.put("key"+i,t);
}
System.out.println("first");
//为dump出堆提供时间
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<1000000;i++){
Person t = new Person();
map.put("key"+i,t);
}
System.out.println("second");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<3000000;i++){
Person t = new Person();
map.put("key"+i,t);
}
System.out.println("third");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<4000000;i++){
Person t = new Person();
map.put("key"+i,t);
}
System.out.println("forth");
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("qqqq");
}
}
实体类
package com.java.book.jvm.chapter4;
import lombok.Data;
@Data
public class Person {
private String name;
private Integer age;
}
配置JVM参数:-Xms512m -Xmx512m -XX:-UseGCOverheadLimit -XX:MaxPermSize=50m
1.查看Visual GC标签,内容如下,这是输出first的截图
2.这是second 输出
3.这是输出forth的截图
通过2张图对比发现:
老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:
增加到了7次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。后面还会继续增加如下
而且程序卡死了
如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:
按照程序输出进行堆dump,当输出second时,dump一次,当输出forth时dump一次。
进入最后dump出来的堆标签,点击类:
结果如下
可以看出在两次间隔时间内Person对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。(这是为什么?)
如何查看对象引用关系呢?
右键选择类Person,选择“在实例视图中显示”,如下所示:
结果如下
左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类CyclicDependencies里面被引用了,并且被HashMap引用。
如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
因为在上面这个例子中,我们是一直往Map里加对象的,没有执行remove对象操作,也没有释放Map,所以通过visualVM可以看到堆占用的内存是很高的,当我执行remove对象操作或把map设为空时, 可以很直观的看到堆内存在GC时,一下子回收了很多对象