我刚刚接受了一次采访,被要求用Java创建内存泄漏。

不用说,我觉得自己很傻,不知道如何开始创作。

什么样的例子?


当前回答

内存泄漏的情况有很多种。我遇到了一个,它暴露了一个不应该在其他地方暴露和使用的地图。

public class ServiceFactory {

    private Map<String, Service> services;

    private static ServiceFactory singleton;

    private ServiceFactory() {
        services = new HashMap<String, Service>();
    }

    public static synchronized ServiceFactory getDefault() {

        if (singleton == null) {
            singleton = new ServiceFactory();
        }
        return singleton;
    }

    public void addService(String name, Service serv) {
        services.put(name, serv);
    }

    public void removeService(String name) {
        services.remove(name);
    }

    public Service getService(String name, Service serv) {
        return services.get(name);
    }

    // The problematic API, which exposes the map.
    // and user can do quite a lot of thing from this API.
    // for example, create service reference and forget to dispose or set it null
    // in all this is a dangerous API, and should not expose
    public Map<String, Service> getAllServices() {
        return services;
    }

}

// Resource class is a heavy class
class Service {

}

其他回答

我认为,一个有效的例子可能是在线程集中的环境中使用ThreadLocal变量。

例如,使用Servlet中的ThreadLocal变量与其他web组件通信,让容器创建线程,并在池中维护空闲线程。ThreadLocal变量如果没有正确清理,将一直存在,直到同一个web组件覆盖它们的值。

当然,一旦确定,问题很容易解决。

从finalize方法引发未处理的异常。

面试官可能一直在寻找一个循环参考解决方案:

    public static void main(String[] args) {
        while (true) {
            Element first = new Element();
            first.next = new Element();
            first.next.next = first;
        }
    }

这是引用计数垃圾收集器的典型问题。然后,您可以礼貌地解释JVM使用了一种更复杂的算法,它没有这种限制。

也许通过JNI使用外部本机代码?

使用纯Java,这几乎是不可能的。

但这是一种“标准”类型的内存泄漏,即您无法再访问内存,但它仍然属于应用程序。相反,您可以保留对未使用对象的引用,或者打开流而不关闭它们。

GUI代码中的一个常见示例是创建小部件/组件并向某个静态/应用程序范围的对象添加侦听器,然后在小部件被破坏时不删除侦听器。不仅会出现内存泄漏,而且性能也会受到影响,因为无论你听什么都会引发事件,所有的老听众都会被调用。