23

有效的 Java 说:

内存泄漏的第三个常见来源是侦听器和其他回调。如果您实现一个 API,其中客户端注册回调但没有显式取消注册,除非您采取一些措施,否则它们将累积。确保回调被及时垃圾回收的最佳方法是仅存储对它们的弱引用,例如,将它们仅作为键存储在 WeakHashMap 中。

我是Java的初学者。有人可以教我如何在回调中创建弱引用并告诉我他们如何解决内存泄漏问题吗?谢谢。

4

3 回答 3

13

阅读这篇文章

关键要点是:

您可以将直接引用视为不需要额外编码即可创建或访问对象的强引用。其余三种类型的引用是 java.lang.ref 包中的 Reference 类的子类。SoftReference 类提供软引用,WeakReference 类提供弱引用,PhantomReference 提供虚引用。

软引用就像一个数据缓存。当系统内存不足时,垃圾收集器可以任意释放其唯一引用为软引用的对象。换句话说,如果没有对对象的强引用,则该对象是释放的候选对象。垃圾收集器需要在抛出 OutOfMemoryException 之前释放所有软引用。

弱引用比软引用弱。如果对象的唯一引用是弱引用,垃圾收集器可以随时回收对象使用的内存。对内存不足的情况没有要求。通常,对象使用的内存会在垃圾收集器的下一次循环中回收。

幻影引用与清理任务相关。它们在垃圾收集器执行终结过程并释放对象之前立即提供通知。将其视为一种在对象内执行清理任务的方法。

接下来是 WeakListModel 列表,我不会发布该列表以避免混淆此响应。

于 2010-05-18T17:19:47.210 回答
9

为了用一个快速(粗略)的例子来说明这个概念,请考虑以下内容:

public interface ChangeHandler {
    public void handleChange();
}

public class FileMonitor {

    private File file;
    private Set<ChangeHandler> handlers = new HashSet<ChangeHandler>();

    public FileMonitor(File file) { 
        this.file = file;
    }

    public void registerChangeHandler(ChangeHandler handler) {
        this.handlers.add(handler);
    } 

    public void unregisterChangeHandler(ChangeHandler handler) {
        this.handlers.remove(handler);
    }

    ...
}

如果客户端类然后使用此FileMonitorAPI,他们可能会这样做:

public class MyClass {

    File myFile = new File(...);
    FileMonitor monitor = new FileMonitor(myFile);

    public void something() {
        ...
        ChangeHandler myHandler = getChangeHandler();
        monitor.registerChangeHandler(myHandler);
        ...
    }
}

如果 then 的作者在处理程序完成MyClass后忘记调用unregisterChangeHandler()FileMonitor'HashSet将永远引用已注册的实例,导致它保留在内存中,直到FileMonitor被销毁或应用程序退出。

为了防止这种情况,Bloch 建议使用弱引用集合而不是HashSet,这样如果您的实例MyClass被销毁,引用将从监视器的集合中删除。

您可以将HashSetin替换为FileMonitoraWeakHashMap并将处理程序用作键,因为后者会在对对象的所有其他引用都消失时自动从集合中删除处理程序。

于 2011-12-12T14:14:03.537 回答
1

在这里您也可以找到一个清晰实用的 解释Android 中的内存泄漏——识别、处理和避免

于 2016-07-22T11:43:43.520 回答