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

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

什么样的例子?


当前回答

我最近遇到了由log4j引起的内存泄漏情况。

Log4j有一种称为嵌套诊断上下文(NDC)的机制,它是一种区分不同来源的交织日志输出的工具。NDC工作的粒度是线程,因此它区分不同线程的日志输出。

为了存储线程特定的标记,log4j的NDC类使用一个Hashtable,该Hashtable由thread对象本身(而不是线程id)键控,因此直到NDC标记保留在内存中,挂在线程对象上的所有对象也保留在内存。在我们的web应用程序中,我们使用NDC标记带有请求id的登录,以将日志与单个请求区分开来。将NDC标记与线程关联的容器在返回请求响应时也会将其删除。在处理请求的过程中,产生了一个子线程,类似于以下代码:

pubclic class RequestProcessor {
    private static final Logger logger = Logger.getLogger(RequestProcessor.class);
    public void doSomething()  {
        ....
        final List<String> hugeList = new ArrayList<String>(10000);
        new Thread() {
           public void run() {
               logger.info("Child thread spawned")
               for(String s:hugeList) {
                   ....
               }
           }
        }.start();
    }
}    

因此,NDC上下文与派生的内联线程相关联。这个NDC上下文的关键线程对象是一个内联线程,它挂着hugeList对象。因此,即使线程完成了它正在做的事情,对hugeList的引用也会被NDC上下文Hastable保持活动状态,从而导致内存泄漏。

其他回答

答案完全取决于面试官认为他们在问什么。

在实践中是否可能造成Java泄漏?当然是这样,其他答案中有很多例子。

但有很多元问题可能被问到了?

理论上“完美”的Java实现是否容易泄漏?候选人是否理解理论与现实之间的区别?应聘者是否了解垃圾收集的工作原理?或者垃圾收集在理想情况下应该如何工作?他们知道他们可以通过本地接口调用其他语言吗?他们知道用其他语言泄露内存吗?应聘者是否知道什么是内存管理,以及Java的幕后情况?

我把你的元问题理解为“在这种面试情况下我可以用什么答案”。因此,我将重点关注面试技巧,而不是Java。我相信,你更可能重复在面试中不知道问题答案的情况,而不是你需要知道如何使Java泄漏。所以,希望这会有所帮助。

你可以培养的面试最重要的技能之一是学会积极倾听问题,并与面试官合作以提取他们的意图。这不仅可以让你以他们想要的方式回答他们的问题,还表明你有一些重要的沟通技巧。当要在许多同样有才华的开发人员之间做出选择时,我会雇佣一个在他们每次回应之前都能倾听、思考和理解的人。

JDK 1.7之前内存泄漏的实时示例:

假设您读取了一个包含1000行文本的文件,并将其保存在String对象中:

String fileText = 1000 characters from file
fileText = fileText.subString(900, fileText.length());

在上面的代码中,我最初读取了1000个字符,然后执行了子字符串,只获得最后100个字符。现在,fileText应该只引用100个字符,所有其他字符都应该被垃圾收集,因为我丢失了引用,但是在JDK1.7之前,substring函数间接引用了最后100个字符的原始字符串,并阻止了整个字符串的垃圾收集,而整个1000个字符将一直保存在内存中,直到您丢失了对子字符串的引用。

您可以创建一个类似于上述的内存泄漏示例。

任何时候,只要您保留对不再需要的对象的引用,就会出现内存泄漏。请参阅处理Java程序中的内存泄漏,以了解内存泄漏如何在Java中表现出来以及您可以如何处理它。

创建一个静态映射并不断添加硬引用。这些永远不会被垃圾收集。

public class Leaker {
    private static final Map<String, Object> CACHE = new HashMap<String, Object>();

    // Keep adding until failure.
    public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}

在Java中,“内存泄漏”主要是因为您使用了太多内存,这与在C中不同,在C中,您不再使用内存,而是忘记返回(释放)内存。当面试官询问Java内存泄漏时,他们询问的是JVM内存使用情况,但似乎一直在增加,他们认为定期重新启动JVM是最好的解决方案(除非面试官非常精通技术)。

所以,回答这个问题,就像他们问JVM内存使用量随时间增长的原因一样。好的答案是在HttpSessions中存储太多数据,超时时间过长,或者内存缓存(singleton)实现不佳,从不刷新旧条目。另一个可能的答案是拥有大量JSP或动态生成的类。类被加载到一个名为PermGen的内存区域,该区域通常很小,大多数JVM不实现类卸载。