主要有以下原因:对象定义在错误的范围
异常处理不当
集合数据管理不当
对象定义在错误的范围
分析下面的代码:
public class Memory {private String[] names;public void handle(int length) {names = new String[length];System.out.println(names);}}
names 仅仅作为简单的使用,仅在 handle 方法中使用,由于 Memory 的实例对象持有 names 对象,只要该实例不被回收,那么names 实例也不会被回收,出现临时性内存泄漏,修改为下面的代码更合适
public class Memory {public void handle(int length) {String[] names = new String[length];System.out.println(names);}}
异常处理不当
public class Memory {public void handle(int length) {InputStream inputStream = null;try {doSomething();inputStream.close();} catch (Exception e) {// 若doSomething 出现内存泄漏,会导致close方法不会正常被调用// 导致inputStream 不会被正确回收,导致内存泄漏}}}
集合数据处理不当
基于数组的结构,尽量减少resize , 因为 resize的时候会涉及到数据拷贝,以及内存碎片的问题
如果集合只需要顺序访问,而非随机访问,可以使用LinkedList 代替ArrayList ,因为链表不需要resize
ThreadLocal使用不当
在线程池中使用ThreadLocal的时候要特别注意,在用完之后一定要手动的移除,否则可能造成内存泄漏,可以查看下面的代码
public static void main(String[] args) {sleep(10_000);ExecutorService executorService = Executors.newFixedThreadPool(1);executorService.submit(() -> {ThreadLocal f = new ThreadLocal<>();f.set(new byte[30 * 1024 * 1024]);sleep(1_000);f.remove(); ## 手动移除System.out.println("任务执行完。。。。");});sleep(3_000);System.out.println("执行GC2");System.gc();sleep(10_000);}
这是由于线程池执行任务的时候,复用线程,当在任务中设置ThreadLocal的时候,其实是为某个线程设置ThreadLocl,所以如果此线程不关闭,那么则线程会一直持有此对象,造成内存泄漏,无法回收! 更多关于线程池,ThreadLocal的知识,可以查看笔者的 多线程的应用-异步任务线程池的简单实现 这篇博文,通过实现一个简单的线程池来学些线程池的相关知识。