Java隐式地通过GC(守护线程)回收内存。 GC定期检查是否存在无法访问的对象,或者确切地说,没有指向该对象的引用。如果是这样,GC回收新可用的内存。
现在的问题是我们应该担心内存泄漏还是Java如何处理它?
注意定义:当对象不可达(未使用)时或没有活动的线程可以访问它时,此对象可被作为垃圾进行回收。
因此,如果在应用程序中有未使用的引用,但此引用无意中被对象持有,则不符合垃圾回收的条件,这就是潜在的内存泄漏。
GC处理不可达的对象,但无法确定未使用的对象。未使用的对象取决于应用程序逻辑,因此程序员必须注意业务代码。
内存泄漏可能会以许多方式发生,我将看一些例子。
示例1:自动装箱
你能发现内存泄漏吗?
这里我犯了一个错误。而不是将基本数据类型用于求和,我采用了Long(包装类),这是内存泄漏的原因。由于自动装箱,sum = sum + l;在每次迭代中创建一个新对象,因此将创建1000个不必要的对象。请避免在基本数据类型和包装类之间进行混合使用。
尽可能地使用基本的数据类型。
示例2:使用缓存
在这里,由于内部map数据结构而发生内存泄漏。此类用于显示缓存中的员工值。一旦显示完,就不需要将这些元素存储在缓存中。
我们忘记清除缓存,所以尽管应用程序不再需要缓存中的对象,但是它不能被GC回收,因为map对它们有很强的引用。
因此,当您使用自己的缓存时,如果不再需要缓存中的项目,请不要忘记清除它们。或者,您可以通过WeakHashMap初始化缓存。 WeakHashMap的优点是,如果key未被任何其他对象引用,则该条目将符合GC标准。
关于WeakHashMap需要谨慎的使用,如果要重新使用存储在缓存中的值,可能是它的key不被任何其他对象引用,因此该条目将被GC回收并且该值奇迹般地消失了。
示例3:关闭连接
在上面的例子中,我们关闭了try块中的连接(Costly)资源,所以在异常的情况下,连接不会被关闭。所以它会创建一个内存泄漏,因为这个连接永远不会返回到池中。
请始终把任何关闭的东西放在finally块中。
示例4:使用CustomKey
在CustomKey中,我们忘记提供equals()和hashcode()实现,因此映射get()方法检查hashcode()和equals()时,不能再检索存储在map中的键和值。但是这个条目不能被GC回收,因为map引用了它,但应用程序无法访问它。绝对是内存泄漏。
所以当你做自定义key时,总是提供一个equals和hashcode()的实现。
示例5:可修改的CustomKey
虽然这里我们为自定义key提供了equals()和hashcode(),但是在将其存储到map中后,我们无意中使得它可变。如果它的属性被更改,则该条目将永远不会被应用程序找到,但是map保存一个引用,所以发生内存泄漏。
始终使您的自定义key不变。
示例6:内部数据结构
这里我们面临一个棘手的问题,当Stack第一次增长然后收缩。实际上是由于内部的实现。堆栈内部保存一个数组,但是从应用程序的角度来看,Stack的活动部分是指针指向的位置。
所以当Stack增长到1000时,内部的数组单元格填满了元素,但之后当我们弹出所有元素时,指针变为零,所以根据应用程序它是空的,但内部数组包含所有弹出的引用。
在Java中,我们将其称为过时引用。过时的引用是不能取消的引用的引用。 该引用不能被GC回收,因为数组包含这些元素,但是在弹出后不必要。
要修复它,我们需要在弹出操作发生时设置空值,以便这些对象能够被GC回收。
防止内存泄漏的安全措施: