1概述
可达性分析判断对象是否不再被使用,但由于代码的实现不同会出现很多内存泄露问题(jvm误以为此对性还在引用,无法被回收)
-
是否还被使用? 是
-
是否还被需要? 否
此时就是内存泄露
2内存泄露的理解
严格来说,只有对象不会在被程序用到了,但是gc又不能回收他们的情况,叫做内存泄露。
但是实际情况由于一些不好的代码习惯导致的对象生命周期很长甚至导致oom可以称为宽泛意义上的“内存泄露”
3 内存泄露和内存溢出的关系
- 内存泄露
申请了内存用完了不释放
- 内存溢出
申请内存时,没有足够的内存可以使用
内存泄漏的增多,最终会导致内存溢出
4 内存泄漏的八种情况
- 静态集合类
如hashmap,linkedlist,如果这些容器为静态的,则他们的生命周期与jvm程序一致,且容器中的对象在程序结束前是无法进行释放的,会造成内存泄露
public class MemoryLeak{
static List list = new ArrayList();
public void oomTests(){
Object obj = new Object();//局部变量
list.add(obj);
}
}
-
单例模式
和静态集合导致的内存泄露原因类似,由于单例静态特性(其生命周期和jvm生命周期一样长)如果单例对象持有外部对象的引用,则外部对象无法被回收 -
内部类持有外部类
内部类持有外部类,如果这个内部类的对象被长期引用,即时外部类对象不再被使用,也无法被垃圾回收。 -
各种连接,数据库连接,网络连接和io连接等
这些连接当不再使用时,需要手动调用close方法来释放连接(因为gc不会回收此连接对象),如果不进行显性关闭,则会造成大量对象无法被回收,造成内存泄露。
//数据库连接
public void oomTest(){
try{
Connection conn =null;
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("url","","");
Statement stmt= conn.createStatement();
ResultSet rs=stmt.executeQuery("....");
}catch(Exception e){
//异常日志
}finally{
//1.关闭结果集 statement
//2 关闭声明的对象ResultSet
//3 关闭连接Connection
}
}
- 变量作用域不合理
一个变量的定义作用范围大于其使用范围,很可能会导致内存泄露。此外如果没有及时将对象设置为null,也会有可能导致内存泄露。
public class UsingRandom{
private String msg;
public void receiveMsg(){
readFormnet();//从网络中接受数据保存到msg中
saveDB();
}
}
- 改变hash值
当一个对象被存储进HashSet集合后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改导致与hashset集合中hash值不同,即找不到对象
这也是String为什么要被设置成不可变类型,我们可以放心把String存入HashSet,或者当做HashMap的key值
- 缓存泄露
将对象引用放入到缓存中时,他就很容易遗忘,因此我们可以使用WeakHashMap代表缓存,此种map的特点是,当除了自身对key引用外,此key没有其他引用因此map自动丢弃此值。
- 监听器回调