背景
Java的内存管理由JVM完成,GC和并发、解释器是JVM主要的三个模块。不像C++需要自己来alloc内存和释放内存来完成对象生命周期管理,Java根据对象的引用情况在适当的时间由JVM决定开始GC或在程序中手动触发GC。内存泄漏发生在需要alloc内存,而内存中已经无可GC的内存的情况下。
常见内存泄漏场景
巨大的静态对象
例如在类型中定义了巨大的static对象。由于该对象的生命周期直接与class的生命周期相关而无法被JVM回收。
解决办法自然是避免使用大的静态对象。
String.intern的使用
先说一下intern的发展历史方便理清思路。
- Java 6之前和它的一些早期版本,String pool是在perm内存空间里且不可变的。
- Java 6u30开始变得可以在程序运行之初改变。
- Java 7开始设置在heap中且可以在运行之初设置,当然有一个默认的size。
- Java 8将这个默认的size变大了。
可以看出,不管是在perm还是heap上,即使可设置,这个size在程序运行时都是fix的。都是有可能在运行时发生outOfMemoryError的。只不过JVM演化过程中将更多的配置能力赋予程序运行者。
由此,我们不难看出,解决办法是使用可设置pool大小的JDK版本,对程序的业务逻辑有清晰的认识从而预设一个合理的初始值。无论哪个版本,String pool的实现都是一个Map,因此,只需按照需求对这个初识值做一个线性估计即可。
没有关闭资源对象
这种curser,stream对象会用到缓存从而占用内存。比较需要注意的是,并不是把对象设置成null就可以解决这个问题,而是需要把他们关闭。因为即使设置null,底层资源还是在被打开的状态,有些资源也不在Java层。因此必须先close然后在设成null。对于Java 8以后,可以用try-with-close block来自动关闭,这只是一个语法糖。
总结
造成内存泄漏的原因有很多,但总结起来无非就是预防和良好的编程风格,该关闭的及时关闭,不用的对象手动复位,注意自己往容器里扔的对象的大小。
网友评论