Java内存泄漏最全详解(六大原因及解决方案)

数据库 其他数据库
在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。

内存泄漏的原因

JVM 虚拟机是使用引用计数法和可达性分析来判断对象是否可回收,本质是判断一个对象是否还被引用,如果没有引用则回收。

在开发的过程中,由于代码的实现不同就会出现很多种内存泄漏问题,让gc 系统误以为此对象还在引用中,无法回收,造成内存泄漏。

内存泄漏的危害

  • 长时间运行,程序变卡,性能严重下降
  • 程序莫名其妙挂掉
  • OutOfMemoryError错误
  • 乱七八糟的错误,还不易排查

内存泄漏有哪些情况

内存泄漏原因很多,因此这里给出最常见的几种。

1.资源未关闭造成的内存泄漏

各种连接,比如数据库连接、网络连接和IO连接等。

在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。

只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏,因此最好按照下面的做法处理资源类,伪代码如下:

publicvoidhandleResource {
try {
// open connection
// handle business
} catch (Throwable t) {
// log stack
} finally {
// close connection
}}

2.静态集合类

如HashMap、LinkedList等等,如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。

生命周期长的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

3.ThreadLocal的误用

ThreadLocal一定要列在Java内存泄露的榜首,总能在不知不觉中将内存泄露掉,一个常见的例子是:

publicvoidtestThreadLocalMemoryLeaks {
ThreadLocal<List<Integer>> localCache = new ThreadLocal<>;
List<Integer> cacheInstance = new ArrayList<>(10000);
localCache.set(cacheInstance);localCache = new ThreadLocal<>;
}

当localCache的值被重置之后cacheInstance被ThreadLocalMap中的value引用,无法被GC。

但是其key对ThreadLocal实例的引用是一个弱引用,本来ThreadLocal的实例被localCache和ThreadLocalMap的key同时引用,但是当localCache的引用被重置之后。

则ThreadLocal的实例只有ThreadLocalMap的key这样一个弱引用了,此时这个实例在GC的时候能够被清理。

图片

上面这张图详细地揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系。

1)Thread中有一个map,就是ThreadLocalMap

2)ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。

3)ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收

重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。

那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。

解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

4.变量不合理的作用域

一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。

public class UsingRandom {
private String msg;
public void receiveMsg(){
readFromNet();// 从网络中接受数据保存到msg中
saveDB();// 把msg保存到数据库中
}
}

如上面这个伪代码,通过readFromNet方法把接受的消息保存在变量msg中,然后调用saveDB方法把msg的内容保存到数据库中,此时msg已经就没用了,由于msg的生命周期与对象的生命周期相同,此时msg还不能回收,因此造成了内存泄漏。

实际上这个msg变量可以放在receiveMsg方法内部,当方法使用完,那么msg的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完msg后,把msg设置为null,这样垃圾回收器也会回收msg的内存空间。

5.内部类持有外部类

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

6.堆外内存无法回收

堆外内存不受gc的管理,可能因为第三方的bug出现内存泄漏

内存泄漏的解决办法

1.少使用静态变量

尽量减少使用静态变量,或者使用完及时 赋值为 null;

2.明确对象有效作用域

明确内存对象的有效作用域,尽量缩小对象的作用域,能用局部变量处理的不用成员变量,因为局部变量弹栈会自动回收;

3.注意声明周期引用

减少长生命周期的对象持有短生命周期的引用;

4.注意Sting的使用

使用StringBuilder和StringBuffer进行字符串连接,Sting和StringBuilder以及StringBuffer等都可以代表字符串,其中String字符串代表的是不可变的字符串,后两者表示可变的字符串。

如果使用多个String对象进行字符串连接运算,在运行时可能产生大量临时字符串,这些字符串会保存在内存中从而导致程序性能下降。

5.不需要对象手动设置Null

对于不需要使用的对象手动设置null值,不管GC何时会开始清理,我们都应及时的将无用的对象标记为可被清理的对象;

6.及时关闭各种链接

各种连接(数据库连接,网络连接,IO连接)操作,务必显示调用close关闭。

责任编辑:武晓燕 来源: mikechen的互联网架构
相关推荐

2016-12-15 21:47:11

Android内存泄漏

2016-08-22 08:36:14

ReactiveCoc内存泄漏GitHub

2020-06-17 07:00:00

Java数据科学家

2014-12-03 10:14:11

Node.js

2014-12-02 09:57:41

Node.js

2018-05-30 08:10:34

智慧农业物联网物联网应用

2023-02-06 10:37:50

数据驱动IT领导者

2017-08-08 16:35:26

Python爆红原因

2022-07-21 15:41:30

OceanStor

2013-08-12 09:51:23

周鸿祎互联网

2023-10-13 08:12:27

2016-11-29 16:29:25

国产存储失败

2014-03-18 10:56:40

2018-10-12 14:34:13

2009-07-06 09:16:30

ERP人才流失

2009-01-11 09:23:00

2013-08-07 10:16:43

Android内存泄漏

2011-01-04 09:20:00

2023-08-28 14:13:08

点赞
收藏

51CTO技术栈公众号