消除过期的对象引用
1.只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。如果一个栈先是增长,然后再收缩,那么,从栈中逃出来的对象将不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。这是因为栈内部维护着这些对象的过期引用。所谓的过期引用,是指永远也不会再被解除的引用
2.内存泄漏的另一个常见来源是缓存。只要在缓存之外存在对某个项的键的引用,该项就有意义,那么就可以用WeakHashMap代表缓存;当缓存中的项过期之后,它们就会自动被删除。记住只有当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才有用处。
更为常见的情形则是,“缓存项的生命周期是否有意义”并不是很容易确定,随着时间的推移,其中的项会变得越来越没有价值。在这种情况下,缓存应该时不时地清除掉没用的项,这项清除工作可以由一个后台线程(可能是ScheduledThreadPoolExecutor)来完成,或者也可以在给缓存添加新条目的时候顺便进行清理。LinkedHashMap利用它的removeEldestEntry方法可以很容易地实现后一种方案,对于更加复杂的缓存,必须直接使用java.lang.ref
3.内存泄漏的第三个常见来源是监听器的其他回调。
如果实现了一个API,客户端会在这个API中注册回调,却没有显式地取消注册,那么除非你采取某些动作,否则它们就会不断地堆积起来,确保回调立即被当作垃圾回收地最佳方法是只保存它们的弱引用,例如,只将他们保存成WeakHashMap中的键
避免使用终结方法和清除方法
1.终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的
2.清除方法没有终结方法那么危险,但仍然是不可预测、运行缓慢,一般情况下也是不必要的
3.终结方法和清除方法有一个非常严重的性能损失,用终结方法和清除方法比try-with-resources慢很多
4.终结方法有一个严重的安全问题:它们为终结方法攻击打开了类的大门。从构造器抛出异常,应该足以防止对象继续存在,有了终结方法的存在,这一点就做不到了。这种攻击可能造成致命的后果。final类不会受到终结方法攻击,因为没有人能够编写出final的恶意子类。为了防止非final类受到终结方法攻击,要编写一个空的final的finalize方法
5.不用编写终结方法或者清除方法,需要让类实现AutoCloseable,并要求其客户端在每个实例不需要的时候调用close方法,一般是利用try-with-resource确保终止,即使遇到异常也是如此。
6.好处1:当资源的所有者忘记调用它的close方法时,终结方法或者清除方法可以充当“安全网”。
7.好处2:与本地对象的本地对等体有关,本地对等体是一个本地(非java)对象,普通对象通过本地方法委托给一个本地对象。本地对象不是一个普通对象,因此垃圾回收器不会知道它,清除方法或者终结方法正是执行这项任务最合适的工具。
try-with-resource优先于try-finally
1.两层嵌套的try-finally只能打印外层的异常
2.try-with-resource:要使用这个构造的资源,必须先实现AutoCloseable接口,其中包含了单个返回void和close方法。
3.try-with-resouce可以使用catch子句,可以处理异常
4.try-with-resource得到的代码更加简洁、清晰,产生的异常也更有价值。