(1)什么叫内存泄漏、内存溢出
内存泄漏:分配出去的内存无法回收,java中的内存泄漏就是给对象分配资源后无法被GC回收
//例:对象有引用,GC Roots可达,无用
public class Test {
Test2 test2 = new Test2();
}
public class Test2{
Test test = new Test();
}
内存溢出:程序要求的内存超出了系统所能分配的范围(如:栈满入栈,占空出栈)
//例:栈满入栈
public static void main(String[] args) {
int [] num = new int[5];
for(int count = 0 ;count < 6; count ++){
num[count] = count;
}
}
//例:栈空出栈
public static void main(String[] args) {
int [] num = new int[0];
System.out.println(num[0]);
}
(2)二者的关系
内存泄漏的堆积最终会导致内存溢出,但从上述例子中可以看出内存泄漏并不是内存溢出的唯一因素
(3)内存泄露的几种场景
1、静态集合类
如 static HashMap。若容器为静态的,那么它们生命周期与程序一致,
容器中的对象不管其是否有用在程序结束之前将不能被释放,从而造成内存泄漏
2、未被释放连接
如数据库连接、IO连接等,如果使用结束之后不关闭其连接会造成大量对象无法回收,从而引发内存泄漏
3、变量不合理的作用域。
例:下述伪代码中 a对象只在方法fishCount()方法中使用,之后便无用了,但因a为类变量,
此时a还不能回收,因此造成了内存泄漏
String a = new String("两条鱼");
public static void main(String[] args) {
new Test().fishCount();
System.out.println("我爱吃鱼");
}
public void fishCount(){
System.out.println("我有"+a);
}
4、内部类持有外部类
如果一个外部类的实例对象的方法返回了一个内部类的实例对象,
这个内部类对象被长期引用了(设置为static引用),
即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,
这个外部类对象将不会被垃圾回收,造成内存泄露。
例:
public class A {
B b = new B();
class B{
}
public void setB_C(){
new C(b);
}
}
public class C{
private static B cb;
c(B ab){
this.cb=ab
}
}
5、改变哈希值
例:
public class Test {
int age;
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
return 1+age;
}
}
public class Test2 {
public static void main(String[] args) {
Test test = new Test();
Set set = new HashSet();
set.add(test);
//存入对象后重新修改其hash值
test.setAge(2);
//无法找到其对象 返回false 造成内存泄漏
System.out.println(set.contains(test));
//无法删除其对象,返回false 造成内存泄漏
System.out.println(set.remove(test));
}
}