内存泄漏、内存溢出、CPU飙升三者之间的关系
内存泄露可能会导致内存溢出。 内存溢出会抛出异常,内存泄露不会抛出异常,大多数时候程序看起来是正常运行的。 内存泄露的程序,JVM频繁进行FullGC尝试释放内存空间,进而会导致CPU飙升 内存泄露过多,造成可回收内存不足,程序申请内存失败,结果就是内存溢出。
内存溢出案例分析
一般来说内存溢出主要分为以下几类:
堆溢出(java.lang.OutOfMemoryError: Java heap space) 最常见最复杂情况
栈深度不够( java.lang.StackOverflowError) 需关注配置项 -Xss大小
栈线程数不够(java.lang.OutOfMemoryError: unable to create new native thread)
1、哪些会造成OOM(内存泄漏)?
(1)ThreadLocal的错误使用导致内存泄漏
ThreadLocal可能引起的OOM内存溢出问题简要分析
我们知道ThreadLocal变量是维护在Thread内部的,这样的话只要我们的线程不退出,对象的引用就会一直存在。当线程退出时,Thread类会进行一些清理工作,其中就包含ThreadLocalMap,Thread调用exit方法,但是,当我们使用线程池的时候,就意味着当前线程未必会退出(比如固定大小的线程池,线程总是存在的)。如果这样的话,将一些很大的对象设置到ThreadLocal中(这个很大的对象实际保存在Thread的threadLocals属性中),这样的话就可能会出现内存溢出的情况。
一种场景 就是说如果使用了线程池并且设置了固定的线程,处理一次业务的时候存放到
ThreadLocalMap中一个大对象,处理另一个业务的时候,又一个线程存放到ThreadLocalMap中一个大对象,但是这个线程由于是线程池创建的他会一直存在,不会被销毁,这样的话,以前执行业务的时候存放到ThreadLocalMap中的对象可能不会被再次使用,但是由于线程不会被关闭,因此无法释放Thread 中的ThreadLocalMap对象,造成内存溢出。
也就是说,ThreadLocal在没有线程池使用的情况下,正常情况下不会存在内存泄露,但是如果使用了线程池的话,就依赖于线程池的实现,如果线程池不销毁线程的话,那么就会存在内存泄露。
故事线:
OOM => FullGC
ThreadLocal + 线程池
① 在没有使用线程池时,使用ThreadLocal并没有问题
② 在使用线程池后,发生了内存泄漏,最终排查到发生了fullGC
③ 排查之后,发现不合理点:》不能将大对象全部放到ThreadLocal中
》线程执行结束后一定要执行remove方法
知识点:① 线程池原理 ② ThreadLocal实现原理、存储原理、数据结构 ③ FullGC相关JVM
具体讲解:
① ---->线程池原理:
线程池状态转移过程:
② ThreadLocal实现原理、存储原理、数据结构