使用Web爬虫耗尽堆空间

时间:2012-07-19 16:05:34

标签: java garbage-collection out-of-memory

我编写了一个小型爬虫,发现它的堆空间不足(即使我目前将列表中的URL数限制为300个)。

使用Java Memory Analyzer,我发现消费者是char[](64MB中有45MB,如果我增加允许的大小,也会更多;它会不断增长)。

分析仪还为我提供了char[]的内容。它包含由抓取工具读取的HTML页面。

通过对-Xmx[...]m的不同设置进行更深入的分析,我发现Java使用几乎所有空间它已经可用,然后在我想要时立即获取out of heap下载3MB大小的图像。

当我给Java 16MB时,它使用14MB并且失败,当我给它64MB它使用59MB并且在尝试下载大图像时失败。

使用这段代码(编辑并添加.close())完成阅读页面:

private String readPage(Website url) throws CrawlerException {
    StringBuffer sourceCodeBuffer = new StringBuffer();
    try {
        URLConnection con = url.getUrl().openConnection();
        con.setConnectTimeout(2000);
        con.setReadTimeout(2000);

        BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String strTemp = "";
        try {
            while(null != (strTemp = br.readLine())) {
                sourceCodeBuffer = sourceCodeBuffer.append(strTemp);
            }
        } finally {
            br.close();
        }
    } catch (IOException e) {
        throw new CrawlerException();
    }

    return sourceCodeBuffer.toString();
}

另一个函数在while循环中使用返回的字符串,但据我所知,一旦字符串被下一页覆盖,就应该释放空格。

public void run() {
    boolean stop = false;

    while (stop == false) {
        try {
            Website nextPage = getNextPage();

            String source = visitAndReadPage(nextPage);
            List<Website> links = new LinkExtractor(nextPage).extract(source);
            List<Website> images = new ImageExtractor(nextPage).extract(source);

            // do something with links and images, source is not used anymore
        } catch (CrawlerException e) {
            logger.warning("could not crawl a url");
        }
    }
}

以下是分析仪给我的输出示例。当我想看哪里仍然需要这些char[]时,分析师无法分辨。所以我猜他们不再需要了,应该是垃圾收集。由于它总是稍微低于最大空间,所以Java 似乎垃圾收集,但只是为了保持程序现在运行所需的数量(不考虑可能会有大量输入到来) )。

此外,每隔5秒或甚至在设置System.gc()后明确调用source = null;无效。

网站代码似乎只是以任何方式存储。

我是否使用了similar to ObjectOutputStream强制读取字符串永久维护的内容?或者Java如何将这些网站Strings保存在char[]数组中这么长时间?

Class Name                                                                                                                                                                                                                                                                                   | Shallow Heap | Retained Heap | Percentage
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
char[60750] @ 0xb02c3ee0  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.512 |       121.512 |      1,06%
char[60716] @ 0xb017c9b8  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.448 |       121.448 |      1,06%
char[60686] @ 0xb01f3c88  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.384 |       121.384 |      1,06%
char[60670] @ 0xb015ec48  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.352 |       121.352 |      1,06%
char[60655] @ 0xb01d5d08  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.328 |       121.328 |      1,06%
char[60651] @ 0xb009d9c0  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.320 |       121.320 |      1,06%
char[60637] @ 0xb022f418  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Wallpaper Kostenlos - 77.777 E-Wallpapers: Widescreen, 3D, Handy, Sexy Frauen</title><link rel="shortcut icon" href="http://img.e-wallp...|      121.288 |       121.288 |      1,06%

修改

使用更多内存对其进行测试后,我在dominator tree

中发现了这种URL
Class Name                                                                                                                                                                                                                                                                                              | Shallow Heap | Retained Heap | Percentage

crawling.Website @ 0xa8d28cb0                                                                                                                                                                                                                                                                           |           16 |       759.776 |      0,15%
|- java.net.URL @ 0xa8d289c0  https://www.google.com/recaptcha/api/image?c=03AHJ_VuuT4CmbxjAoKzWEKOqLaTCyhT-89l3WOeVjekKWW81tdZsnCvpIrQ52aLTw92rP-EUP9ThnzwBwHcRLXG6A0Bpwu11cGttRAUtarmWXhdcTVRoUMLNnJNZeuuA7LedgfTou76nl8ULyuIR3tgo7_lQ21tzzBhpaTSqwYHWyuZGfuRK3z9pgmqRqvI7gE4_4lexjYbkpd62kN...       |           56 |       759.736 |      0,15%
|  |- char[379486] @ 0xa8c6f4f8  <!DOCTYPE html><html lang="en">  <head>  <meta charset="utf-8">  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9">  <title>Google Accounts</title><style type="text/css">  html, body, div, h1, h2, h3, h4, h5, h6, p, img, dl,  dt, dd, ol, ul, li, t...    |      758.984 |       758.984 |      0,15%
|  |- java.lang.String @ 0xa8d28a40  /recaptcha/api/image?c=03AHJ_VuuT4CmbxjAoKzWEKOqLaTCyhT-89l3WOeVjekKWW81tdZsnCvpIrQ52aLTw92rP-EUP9ThnzwBwHcRLXG6A0Bpwu11cGttRAUtarmWXhdcTVRoUMLNnJNZeuuA7LedgfTou76nl8ULyuIR3tgo7_lQ21tzzBhpaTSqwYHWyuZGfuRK3z9pgmqRqvI7gE4_4lexjYbkpd62kNBZ7UIDccO5bx6TqFpf-7Sl...|           24 |           624 |      0,00%
|  |  '- char[293] @ 0xa8d28a58  /recaptcha/api/image?c=03AHJ_VuuT4CmbxjAoKzWEKOqLaTCyhT-89l3WOeVjekKWW81tdZsnCvpIrQ52aLTw92rP-EUP9ThnzwBwHcRLXG6A0Bpwu11cGttRAUtarmWXhdcTVRoUMLNnJNZeuuA7LedgfTou76nl8ULyuIR3tgo7_lQ21tzzBhpaTSqwYHWyuZGfuRK3z9pgmqRqvI7gE4_4lexjYbkpd62kNBZ7UIDccO5bx6TqFpf-7Sl...    |          600 |           600 |      0,00%
|  |- java.lang.String @ 0xa8d289f8  c=03AHJ_VuuT4CmbxjAoKzWEKOqLaTCyhT-89l3WOeVjekKWW81tdZsnCvpIrQ52aLTw92rP-EUP9ThnzwBwHcRLXG6A0Bpwu11cGttRAUtarmWXhdcTVRoUMLNnJNZeuuA7LedgfTou76nl8ULyuIR3tgo7_lQ21tzzBhpaTSqwYHWyuZGfuRK3z9pgmqRqvI7gE4_4lexjYbkpd62kNBZ7UIDccO5bx6TqFpf-7Sl6YmMgFC77kWZR7vvZIPkS...|           24 |            24 |      0,00%
|  |- java.lang.String @ 0xa8d28a10  www.google.com                                                                                                                                                                                                                                                     |           24 |            24 |      0,00%
|  |- java.lang.String @ 0xa8d28a28  /recaptcha/api/image                                                                                                                                                                                                                                               |           24 |            24 |      0,00%

从意图来看,我真的很想知道:为什么HTML源代码是java.net.URL的一部分?这是来自我打开的URLConnection吗?

6 个答案:

答案 0 :(得分:2)

我首先尝试在readPage方法的末尾关闭读者和URL连接。如果将此逻辑放在finally子句中,则最好。

保持打开的连接将使用内存,并且根据内部结构,GC可能无法回收它,即使您不再在代码中引用它

更新(基于评论):连接本身没有close()方法,并且当所有连接到它的阅读器关闭时将关闭。

答案 1 :(得分:1)

我不确定您的信息会导致垃圾收集工作无效。在分配更多内存时,你只是耗尽了内存。你说你认为有些对象符合GC的要求,但是JVM没有。我非常确定我相信JVM而不是猜测!

您的应用中某处(其他地方)存在内存泄漏。您正在某个对象的某个位置继续引用某个网页的整个内容。而这正在填补你的空闲记忆。

答案 2 :(得分:0)

  

当我给Java 16MB时,它使用14MB并且失败,当我给它64MB它使用59MB并且在尝试下载大图像时失败。

这并不奇怪,因为你非常接近你的极限。加载后,3 MB映像可以解包为60 MB或更多(解压缩)您可以将最大值增加到1 GB吗?

答案 3 :(得分:0)

可能会在某处阻止垃圾收集。这总是需要纠正才能纠正。我通常从具有堆分析的分析器开始。如果可能的话,写一个加载页面的小测试程序而不是其他。它可以简单地处理包含一些大图片的3-4个网址列表。如果页面包含大图片,如10+ MB,则应该很容易在分析器中找到。最糟糕的情况是使用的库保存了引用。一个小的测试程序将是最好的调试方法。

答案 4 :(得分:0)

您在任何特定时间运行了多少个线程?您在pastebin中发送的char数组似乎是线程本地的(意味着没有泄漏)。您可能会看到的是,如果您同时运行太多,您将自然会耗尽内存。尝试使用2个线程但相同数量的URL运行。

答案 5 :(得分:0)

我发现的另一个可能原因是原始字符串使用的that substring uses the same old large char array。因此,如果您保留子字符串,则会保留完整的字符串。