zoukankan      html  css  js  c++  java
  • Java 消除过期的对象引用

      内存泄漏的第一个常见来源是存在过期引用。

     1 import java.util.Arrays;
     2 import java.util.EmptyStackException;
     3 
     4 public class Stack {
     5     
     6     private Object[] elements;
     7     private int size = 0;
     8     private static final int DEFAULT_INITIAL_CAPACITY = 16;
     9     
    10     public Stack() {
    11         elements = new Object[DEFAULT_INITIAL_CAPACITY];
    12     }
    13     
    14     private void ensureCapacity() {
    15         if (elements.length == size) {
    16             elements = Arrays.copyOf(elements, 2 * size + 1);
    17         }
    18     }
    19     
    20     public void push(Object e) {
    21         ensureCapacity();
    22         elements[size++] = e;
    23     }
    24     
    25     public Object pop() {
    26         if (size == 0) {
    27         throw new EmptyStackException();
    28     }
    29         
    30         return elements[--size];
    31     }
    32     
    33 }

      如果一个栈先是增长,然后再收缩,从栈中弹出来的对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。因为栈内部维护着对这些对象的过期引用(obsolete reference)。过期引用指永远也不会再被解除的引用。在本例中,在elements数组的“活动部分(active portion)”之外的任何引用都是过期的。活动部分指elements中下标小于size的那些元素。

      如果一个对象引用被无意识地保留了,垃圾回收机制不仅不会回收这个对象,而且不会回收被这个对象所引用的所有其他对象。解决方法:一旦对象引用已经过期,只需清空这些引用即可。在本例中,只要一个元素被弹出栈,指向它的引用就过期了。修改如下:

     1 public Object pop() {
     2     if (size == 0) {
     3     throw new EmptyStackException();
     4     }
     5         
     6     Object result = elements[--size];
     7     elements[size] = null; // 清空引用
     8         
     9     return result;
    10 }

      清空过期引用的另一个好处是,如果它们以后又被错误地解除引用,程序会立即抛出NullPointerException异常,而不是悄悄地错误运行下去。尽快地检测出程序中的错误总是有益的。消除过期引用最好的方法是让包含该引用的变量结束其生命周期。如果在最紧凑的作用域范围内定义每一个变量(见第45条),这种情形会自然而然地发生。

      只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,该元素中包含的任何对象引用都应该被清空。

     

      内存泄漏的第二个常见来源是缓存。

      把对象引用放到缓存中,它就很容易被遗忘掉,从而使得它不再有用之后很长一段时间内仍然留在缓存中。用WeakHashMap代表缓存时,当缓存中的项过期之后,它们会被自动地删除。只有当缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才起作用。

      随着时间的推移,缓存项会变得越来越没有价值,缓存应该时不时地清除掉没用的项。可以由一个后台线程(可能是Timer或者ScheduledThreadPoolExecutor)来完成,或者在给缓存添加新条目时进行清理。LinkedHashMap类可以通过它的removeEldestEntry方法来实现后者。对于更加复杂的缓存,必须直接使用java.lang.ref。

      内存泄漏的第三个常见来源是监听器和其他回调。

      如果客户端在自己实现的API中注册回调,却没有显式地取消注册,那么除非自己采取某些动作,否则它们就会积聚。确保回调立即被当作垃圾回收的最佳方法是只保存它们的弱引用(weak reference),例如,只将它们保存为WeakHashMap中的键。

      往往只有通过仔细检查代码,或者借助于Heap剖析工具(Heap Profiler)才能发现内存泄露问题。

      参考资料

      《Effective Java 中文版 第2版》 第6条:消除过期的对象引用 P21-23

  • 相关阅读:
    Jmeterif controller 使用
    转载App测试工具大全
    Jmeter 官方在线文档&Android SDK 官方下载地址
    APP自动化之uiautomator2 +python3 UI自动化
    uiautomatorviewer不支持安卓 9.0或以上,提示:"error: obtaining UI hierachy"解决方法
    调查显示:软件开发公司出现“人才荒”
    浅谈Lean UX:我们到底该怎么设计?
    谷歌工程师再次公布Windows漏洞 并称微软很难合作
    灵活运用AppFlood:提高APP eCPM的10个技巧
    Spring Framework 4.0M1发布,支持JDK 8、Java EE 7
  • 原文地址:https://www.cnblogs.com/WJQ2017/p/7634330.html
Copyright © 2011-2022 走看看