一、概念:
1.内存泄漏:程序申请内存后,无法释放已申请的内存
2.内存溢出:程序申请内存时,没有足够的内存供申请者使用
3.内存泄漏的堆积最终会导致内存溢出
二、内存泄漏的分类
1.常发性内存泄漏:发生内存泄漏的代码会被多次执行,每次执行都会导致一块内存泄漏
2.偶发性内存泄漏:发生内存泄漏额代码只有在特定环境和操作过程下才会发生。常发性和偶发性是相对的。对于特定环境,偶发性也变成常发性。所以测试环境和测试方法很重要
3.一次性内存泄漏:发生内存泄漏的 代码只会执行一次,或者由于算法上的缺陷,总会有一块且仅一块,内存发生泄漏。比如,在类的构造函数中分配内存,在构造函数中卻没有释放该内存,所以内存泄漏只发生一次(例如单例)
4.隐式内存泄漏:程序在运行过程中不断分配内存,至到结束释放内存。严格说美欧发生内存泄漏,因为最终释放了所有申请的内存
三、内存溢出的原因及解决方法:
1.内存溢出原因:
1.1 内存中加载的数据量过于庞大,如一次性从数据库取出过多数据
1.2 集合类中对对象的引用,使用完未清空,使JVM不能回收
1.3 代码死循环或者循环产生过多重复的的对象实体
1.4 使用第三方软件的bug
1.5 启动参数内存值设定的过小
2.内存溢出的解决方法
第一步:修改JVM启动参数,直接增加内存(-Xms,-Xmx参数一定不要忘记加,例如tomcat默认只有90m)
第二步:检查错误日志,查看"OutOfMemory"错误前是否有其他异常或错误(例如本来可以清除的资源文件,或者io流,本来要清除的,可是程序报错,没有清除,后面导致内存溢出)
第三步:对代码进行走查和分析,找出可能发生内存溢出的位置
重点排查以下几点:
2.1 检查对数据库查询中,是否有一次获取全部数据的查询。一般来说,如果一次取十万条记录到内存中,就可能引起内存溢出。这个问题较隐蔽,上线前,数据库数据少,不容易出现问题,上线后,数据量多乐乐,一次查询可能出现问题。因为对数据库查询尽量采用分页的方法查询
2.2 检查代码中是否有死循环,或者递归调用
2.3 检查是否有大循环重复产生新对象实体(尽量在循环体外面,创建对象)
2.4 检查List、Map等集合对象是否有使用完后,未清除的问题(List、Map等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收)例子:往往集合中放的是对象的引用,真正的对象是在堆区里,集合使用完了后,没有对这些引用变量进行清空,导致堆区的对象,始终有个引用指向它,所以垃圾回收器,没办法把有引用的对象清空,久而久之,内存越来越小
第四步:使用内存查看工具动态查看内存使用情况