内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
以java的角度来分析,就是JVM创建的对象永远都无法访问到,但是GC又不能回收对象所占用的内存。少量的内存泄漏并不会出现什么严重问题,无非是浪费了一些内存资源罢了,但是随着时间的积累,内存泄漏的越来越多就会导致「内存溢出」,程序崩溃。GC算中的一种引用计数,当循环依赖时,就会造成内存泄漏。
在大型应用程序中存在各种各样的全局数据仓库是很普遍的,在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。常用的两种管理方式:
1、一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。
2、另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。
缓存一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行需要比较多的资源并会多次被使用,通常做法是把常用的输入数据的操作结果进行缓存,以便在下次调用该操作时使用缓存的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果,因此需要将所使用的内存容量与检索数据的速度加以平衡。
常用的解决途径是将存储对象继承java.lang.ref.SoftReference类放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。
java类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构,因为类装载器不仅仅是只与"常规"对象引用有关,同时也和对象内部的引用有关。比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。
最典型的一个就是java永久区的大小,当使用加载器不断热加载新的类时,若配置了metaspace的大小,就很容易造成内存泄漏。