概念
-
内存泄漏memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
-
内存溢出 out of memory :指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
内存泄漏
第一类:程序中已动态分配的内存由于某种原因未释放或无法释放
-
数据库之类的连接要调用close()方法将其关闭,否则是不会自动被GC 回收的。
-
使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要把设置的key,value,也就是Entry对象进⾏回收,而ThreadLocalMap是通过强引⽤指向Entry对象。当ThreadLocal对象使用完之后,Entry对象无法被回收造成内存泄漏。当然ThreadLocal本身对于key为null的Entry有自清理的过程,但是这个过程是依赖于后续对ThreadLocal的继续使用,如果一直没有使用,那么就一直清理不掉。解决办法是,在使⽤了ThreadLocal对象之后,⼿动调⽤ThreadLocal的remove⽅法,⼿动清除Entry对象
第二类:长生命周期对象持有短生命周期对象的强引用
-
static字段引起的内存透露(尤其是集合类,一般只会添加数据,不会删除数据)。静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放。可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
-
单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收
-
非静态内部类(包括匿名内部类)默认就会持有外部类的引用.当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄漏
内存溢出
- 堆内存溢出
public class Main {
public static void main(String[] args) {
ArrayList<int[]> list=new ArrayList<>();
while(true){
list.add(new int[100000]);
}
}
}
- 栈内存溢出
public class Main {
public static void main(String[] args) {
circle();
}
public static void circle(){
circle();
}
}
内存溢出如何排查
- 导出堆的dump文件,JVM启动时添加参数:
-Xms 1m, -Xmx 8m。缩小堆内存空间,防止堆过大导出dump文件时电脑卡死
+HeapDumpOnOutOfMemoryError。当程序发生堆内存溢出时自动导出堆的dump文件
-XX:HeapDumpPath=/home/xxx/logs/。生成的dump文件的地址 - 使用JProfiler工具打开dump文件,可以查看类所占内存,大对象所占内存,分析是哪一个类或者哪一个对象占用了较高的内存(比如如果不停的向list里面添加数据,那么list这个对象会占用几乎90%的内存)
- 还可以查看线程信息,可以定位到具体是哪一个线程的第几行出现了问题(如图,main线程的第17行出现了问题)