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

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

什么样的例子?


当前回答

每个人都会忘记本机代码路径。以下是泄漏的简单公式:

声明本机方法。在本机方法中,调用malloc。不要打免费电话。调用本机方法。

记住,本机代码中的内存分配来自JVM堆。

其他回答

一些建议:

在servlet容器中使用commons日志记录(可能有点挑衅)在servlet容器中启动线程,不要从其运行方法返回在servlet容器中加载动画GIF图像(这将启动一个动画线程)

通过重新部署应用程序,可以“改善”上述效果;)

我最近偶然发现:

调用“newjava.util.zip。充气器();”而不调用“充气器.end()”

阅读http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5072161并将问题联系起来进行深入讨论。

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

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

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

我曾经有过一次关于PermGen和XML解析的“内存泄漏”。我们使用的XML解析器(我记不清是哪一个)对标记名执行String.intern(),以加快比较速度。我们的一位客户有一个好主意,不将数据值存储在XML属性或文本中,而是将其存储为标记名,因此我们有了这样一个文档:

<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

事实上,他们没有使用数字,而是使用更长的文本ID(约20个字符),这些ID是唯一的,每天的使用率为1000万至1000万。这使得每天有200 MB的垃圾,而这些垃圾再也不需要了,也永远不会被GCed(因为它在PermGen中)。我们将permagen设置为512MB,因此内存不足异常(OOME)需要大约两天的时间才能到达。。。

Java中的内存泄漏不是典型的C/C++内存泄漏。

要了解JVM的工作原理,请阅读了解内存管理。

基本上,重要的部分是:

标记和扫描模型JRockit JVM使用标记和清除垃圾收集模型执行整个堆的垃圾收集。标记和扫描垃圾收集包括两个阶段,标记阶段和扫描阶段。在标记阶段,可以从Java访问的所有对象线程、本机句柄和其他根源标记为活动的,如以及可从这些对象访问的对象,等等向前地此过程识别并标记所有静止的对象使用,其余的可以被视为垃圾。在扫描阶段,将遍历堆以查找活动对象。这些差距记录在免费列表中可用于新对象分配。JRockit JVM使用标记和扫描的两个改进版本模型一种是同时进行标记和扫描,另一种是平行标记和扫描。你也可以将这两种策略结合起来例如主要是并发标记和并行扫描。

因此,在Java中创建内存泄漏;最简单的方法是创建一个数据库连接,做一些工作,而不是Close();然后在保持范围内的同时生成新的数据库连接。例如,这在循环中并不难做到。如果您有一个工作人员从队列中拉出并推送到数据库,那么您可以通过忘记Close()连接或在不需要时打开连接等方式轻松创建内存泄漏。

最终,您将通过忘记Close()连接来消耗已分配给JVM的堆。这将导致JVM垃圾疯狂收集;最终导致java.lang.OutOfMemoryError:java堆空间错误。应该注意,该错误可能并不意味着存在内存泄漏;这可能意味着你没有足够的记忆;例如,Cassandra和Elasticsearch等数据库可能会抛出错误,因为它们没有足够的堆空间。

值得注意的是,所有GC语言都是如此。以下是我作为SRE工作的一些例子:

Node.js使用Redis作为队列;开发团队每12小时创建一次新连接,但忘记关闭旧连接。最终,节点是OOMd,因为它消耗了所有内存。去吧(我犯了这个罪);使用JSON.Unmarshal解析大型JSON文件,然后通过引用传递结果并保持其打开状态。最终,这导致整个堆被我打开以解码JSON的意外引用所消耗。

什么是内存泄漏:

这是由错误或不良设计引起的。这是在浪费记忆。随着时间的推移,情况会变得更糟。垃圾收集器无法清理它。

典型示例:

对象缓存是一个很好的起点,可以让事情变得一团糟。

private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}

您的缓存不断增长。很快整个数据库就被吸进了内存。更好的设计使用LRUMap(仅将最近使用的对象保存在缓存中)。

当然,你可以让事情变得更加复杂:

使用ThreadLocal构造。添加更复杂的参考树。或由第三方库引起的泄漏。

经常发生的情况:

如果此Info对象引用了其他对象,则这些对象也引用了其他的对象。在某种程度上,您也可以认为这是某种内存泄漏(由糟糕的设计导致)。