啥是内存泄漏?
内存泄漏就是堆内存中的某些对象,你虽然不再使用它们了,但是垃圾回收器还回收不了他们,长此以往内存就慢慢耗没了。
内存泄漏怎么检测?
如果你的服务经常占用内存很大,慢慢隔一段时间需要重启一下,说明代码有问题,存在内存泄漏的风险,具体的可以使用三种方式检测:
Heap Memory footprint
Garbage collector activity
heap dump log
内存泄漏实例
此处存在死循环,无法跳出,同时list的内存占用不断增大,无法得到释放,导致OOM
VisualVM是用于分析堆内存占用情况的
从图中就可以看出,堆内存很快就被打满了。
垃圾回收活动分析
从图中可以看出,垃圾回收还是很频繁的,但是内存仍然很快被打满了,这说明出现了内存泄漏。
Heap Dump Log是可以在启动的时候配置的:
+XX:+HeapDumpOnOutOfMemoryError表示开启日志
-XX:HeadpDumpPath用于指定日志文件的存储路径
通过分析Dump日志,我们发现存在大量的对象,他们的类型都是Person
然后看对象的详细数据,发现他们都被一个ArrayList占用着
这样就可以锁定出现问题的位置了。
解决内存泄漏
这里使用i作为计数器,每创建1000个Person对象,就会把ArrayList重置一下,这样,上一轮的1000个对象就不在被引用,垃圾回收器就可以正常回收这些对象了。
经过改造,我们发现内存的占用量忽高忽低,这说明垃圾回收起到了作用,内存泄漏的问题解决了。
cpu和垃圾回收活动占用都不高,说明内存泄露解决了。
如何避免内存泄露?
大对象尽可能复用
不创建无用的对象
无用的对象及时置空
对于网络、数据库、文件、流等资源,open以后一定要close,防止内存泄露。可以使用try...finally句式,或者try with resource模式
字符串的频繁拼接时,注意使用StringBuilder进行替换,因为每次拼接都会产生新的对象,然后存储在字符串常量池,而字符串常量池被分配在对内存中,可能导致堆溢出。
尽可能使用基础类型,避免使用包装类型。包装类型都维护在堆内存中,创建和使用都不如基础类型方便。
不推荐使用静态集合
一方面内存占用不可控,另外资源难以回收,因为对象一直被占用着。