JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让咱们来看一看。java
申请了内存用完了不释放,好比一共有 1024M 的内存,分配了 521M 的内存一直不回收,那么能够用的内存只有 521M 了,仿佛泄露掉了一部分;mysql
通俗一点讲的话,内存泄漏就是【占着茅坑不拉shi】。web
申请内存时,没有足够的内存可使用;sql
通俗一点儿讲,一个厕所就三个坑,有两个站着茅坑不走的(内存泄漏),剩下最后一个坑,厕所表示接待压力很大,这时候一会儿来了两我的,坑位(内存)就不够了,内存泄漏变成内存溢出了。缓存
可见,内存泄漏和内存溢出的关系:内存泄露的增多,最终会致使内存溢出。app
这是一个颇有味道的例子。ide
如上图:测试
对象 X 引用对象 Y,X 的生命周期比 Y 的生命周期长;this
那么当Y生命周期结束的时候,X依然引用着Y,这时候,垃圾回收期是不会回收对象Y的;url
若是对象X还引用着生命周期比较短的A、B、C,对象A又引用着对象 a、b、c,这样就可能形成大量无用的对象不能被回收,进而占据了内存资源,形成内存泄漏,直到内存溢出。
常常发生:发生内存泄露的代码会被屡次执行,每次执行,泄露一块内存;
偶然发生:在某些特定状况下才会发生;
一次性:发生内存泄露的方法只会执行一次;
隐式泄露:一直占着内存不释放,直到执行结束;严格的说这个不算内存泄露,由于最终释放掉了,可是若是执行时间特别长,也可能会致使内存耗尽。
1. 循环过多或死循环,产生大量对象;
2. 静态集合类引发内存泄漏,由于静态集合的生命周期和 JVM 一致,因此静态集合引用的对象不能被释放;下面这个例子中,list 是静态的,只要 JVM 不停,那么 obj 也一直不会释放。
public class OOM {
static List list = new ArrayList();
public void oomTests(){
Object obj = new Object();
list.add(obj);
}
}
复制代码
3. 单例模式,和静态集合致使内存泄露的缘由相似,由于单例的静态特性,它的生命周期和 JVM 的生命周期同样长,因此若是单例对象若是持有外部对象的引用,那么这个外部对象也不会被回收,那么就会形成内存泄漏。
4. 数据链接、IO、Socket链接等等,它们必须显示释放(用代码 close 掉),不然不会被 GC 回收。
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
}
复制代码
5. 内部类的对象被长期持有,那么内部类对象所属的外部类对象也不会被回收。
6. Hash 值发生改变,好比下面中的这个类,它的 hashCode 会随着变量 x 的变化而变化:
public class ChangeHashCode {
private int x ;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ChangeHashCode other = (ChangeHashCode) obj;
if (x != other.x)
return false;
return true;
}
//省略 set 、get 方法
}
复制代码
public class HashSetTests {
public static void main(String[] args){
HashSet<ChangeHashCode> hs = new HashSet<ChangeHashCode>();
ChangeHashCode cc = new ChangeHashCode();
cc.setX(10);//hashCode = 41
hs.add(cc);
cc.setX(20);//hashCode = 51
System.out.println("hs.remove = " + hs.remove(cc));//false
hs.add(cc);
System.out.println("hs.size = " + hs.size());//size = 2
}
}
复制代码
能够看到,在测试方法中,当元素的 hashCode 发生改变以后,就再也找不到改变以前的那个元素了;
这也是 String 为何被设置成了不可变类型,咱们能够放心地把 String 存入 HashSet,或者把 String 当作 HashMap 的 key 值;
当咱们想把本身定义的类保存到散列表的时候,须要保证对象的 hashCode 不可变。
7. 内存中加载数据量过大;以前项目在一次上线的时候,应用启动奇慢直到夯死,就是由于代码中会加载一个表中的数据到缓存(内存)中,测试环境只有几百条数据,可是生产环境有几百万的数据。
会点代码的大叔 | 文【原创】