问题的引出
这段程序有一个“内存泄露”,随着GC活动的增加,或者由于内存占用的不断增加,程序性能降低会逐渐表现出来。在极端的情况下,这种内存泄露会导致磁盘交换,甚至导致程序失败(OutOfMemoryError),但是这种失败情况相对比较少见。
内存泄露的地方:一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。
栈内部维护着对这些对象的过期引用(永远也不会再被解除的引用)。
elements数组中下标小于size的那些元素都是过期的。
无意识的对象保持导致的问题
在支持垃圾回收的语言中,内存泄露是很隐蔽的,无意识的对象保持。
一个对象引用被无意识地保留起来,那么,GC不仅不会处理这个对象,而且也不会处理这个对象所引用的所有其他对象,因此有可能会对性能造成潜在的重大影响。
无意识的对象保持修复方法
一旦对象引用已经过期,只需清空这些引用即可。
清空过期引用的另一个好处是,如果它们以后又被错误地解除引用,程序会立即抛出NullPointerException异常,而不是悄悄地错误运行下去。
清空对象引用应该是一种例外,而不是一种规范行为
清除过期引用最好的办法是让包含该引用的变量结束其生命周期。
清空引用的时机
Stack类自己管理内存。存储池包含了elements数组的元素。数组活动区域中的元素是已分配的,而数组其余部分的元素则是自由的。但是GC并不知道这一点;对于GC而言,elements数组中的所有对象引用都同等有效。只有程序员知道数组的非活动部分是不重要的。程序员可以把这个情况告知GC:一旦数组元素变成了非活动部分的一部分,程序员就手工清空这些数组元素。
只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
内存泄露的另一个常见来源是缓存
只要在缓存之外存在对某个项的键的引用,该项就有意义,那么就可以用WeakHashMap代表缓存;当缓存中的项过期之后,它们就会自动被删除。记住只有当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,才有用处。
"缓存项的生命周期是否有意义"并不是很容易确定,随着时间的推移,其中的项会变得越来越没有价值。在这种情况下,缓存应该时不时地清理掉没用的项。这项清除工作可以由一个后台线程来完成,或在给缓存添加新条目的时候顺便进行清理。
LinkedHashMap类可以使用removeEldestEntry()实现在给缓存添加新条目的时候进行清理。
对于更复杂的缓存,必须直接使用java.lang.ref。
内存泄露的第三个常见的来源是监听器和其他回调。
客户端在API中注册回调,却没有显示地取消注册。
确保回调立即被当做垃圾回收的最佳方法是只保存它们的弱引用,例如,只将它们保存成中的键。
为了让学习变得轻松、高效,今天给大家免费分享一套Java教学资源。帮助大家在成为Java架构师的道路上披荆斩棘。需要资料的欢迎加入学习交流群:9285,05736
网友评论