JAVA中内存泄漏的情况

一、什么是内存泄漏?

这句话可能有点绕:当一个长生命周期的对象持有一个短生命周期的对象的引用时,导致短生命周期的对象无法被回收,而导致我们宝贵的内存被占用,最终OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了。

大家都知道在JAVA中有著名的垃圾回收器GC,那么GC是如何认识垃圾的呢?有两种常用的方式1.引用计数法  2.根可达算法,可无论是引用计数还是根可达算法都是根据引用来判断对象到底是不是一个垃圾,要不要回收它,可现在有这么一种情况  如图所示:



对象A生命周期明明是到T1就结束了(引用应该释放掉,被GC发现应该回收内存),但是生命周期为T2的对象B霸占了对象A的引用导致对象A不能被回收。


2、内存泄漏的具体情况

(1)静态集合类:

如HashMap、LinkedList、ArrayList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收,解决办法是最好不使用静态的集合类,如果使用的话,在不需要容器时要将其赋值为null。

(2)创建的各种连接,数据库连接,IO连接等等

例如我们在连接数据库的时候像JDBC六大步,注册驱动,获取连接,创建连接对象,执行sql,处理结果集,释放资源,这是时候的释放资源调用close()方法就是告诉GC,这个连接对象我不用了,你可以回收了,如果你不释放,GC是无法判定你有没有使用完连接对象的,自然不能帮你释放资源,从而,你在使用数据库的时候会产生大量的对象,对资源进行浪费,导致内存泄漏。

(3)变量不合理的作用域

可以声明为局部变量(在方法中)绝不声明为成员变量(在类中) 因为局部变量是随着方法的调用而创建,随着方法的销毁而销毁,而定义为成员变量是随着类在变化,在方法中用完之后没有设为null值,那么便会随着类一直存在造成内存泄漏。

(4)内部类持有外部类

如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

(5)缓存泄漏

内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘,对于这个问题,可以使用WeakHashMap代表缓存,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值

``

public class Test {

public static void main(String[] args) {

String a =new String("a");

String b =new String("b");

Map map =new HashMap();

map.put(a,"aaa");

map.put(b,"bbb");

Map weakmap =new WeakHashMap();

weakmap.put(a,"aaa");

weakmap.put(b,"bbb");

map.remove(a);

a =null; b =null;

System.gc();

map.forEach((i1,i2)->{

System.out.println("map:");

System.out.println(i1+":"+i2);});

weakmap.forEach((i1,i2)->{

System.out.println("weakmap:");

System.out.println(i1+":"+i2);

});

}

}

``

输出结果如下,看一下代码你发下只是移除了map中的a,weakmap中的a也消失了

(6)改变哈希值

当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露

(7)单例模式可能会造成内存泄露

    单例模式只允许应用程序存在一个实例对象,并且这个实例对象的生命周期和应用程序的生命周期一样长,如果单例对象中拥有另一个对象的引用的话,这个被引用的对象就不能被及时回收。解决办法是单例对象中持有的其他对象使用弱引用,弱引用对象在GC线程工作时,其占用的内存会被回收掉,不知道什么是弱引用的可以看我之前文章,强软弱虚。

(8)监听器和回调

内存泄漏第三个常见来源是监听器和其他回调,如果客户端在你实现的API中注册回调,却没有显示的取消,那么就会积聚。需要确保回调立即被当作垃圾回收的最佳方法是只保存他的若引用,例如将他们保存成为WeakHashMap中的键。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容