2

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

使用 Java 内存分析器,我发现消费者是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 似乎进行了垃圾收集,但只是保持程序运行所需的量(不考虑可能会有大量输入)。

System.gc()此外,每 5 秒甚至设置后显式调用一次source = null;也不起作用。

只要有可能,网站代码似乎就会以任何方式存储。

我是否使用了类似于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

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?

4

6 回答 6

2

我会首先尝试在方法结束时关闭阅读器和 URL 连接readPage。最好将此逻辑放在一个finally子句中。

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

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

于 2012-07-19T16:14:10.513 回答
1

我不确定您的信息是否会得出垃圾收集不起作用的结论。分配更多内存时,您只是内存不足。你说你认为有些对象有资格进行 GC,但 JVM 没有。我很确定我会相信 JVM 而不是猜测!

您的应用程序中某处(其他)存在内存泄漏。您在某个对象的某处保留对网页全部内容的引用。这正在填满你的空闲记忆。

于 2012-07-19T16:23:35.943 回答
0

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

这并不奇怪,因为你已经接近你的极限了。一个 3 MB 的图像在加载(解压缩)时可以解压缩为 60 MB 或更多。您可以将最大值增加到 1 GB 吗?

于 2012-07-19T16:21:36.410 回答
0

很可能引用保存在某处以防止垃圾收集。这总是需要胡乱纠正。我通常从带有堆分析的分析器开始。如果可能,编写一个加载页面的小型测试程序,仅此而已。它可以简单地处理包含一些大图片的 3-4 个 url 列表。如果页面包含一张大图片,比如 10+ MB,那么在分析器中应该很容易找到。最坏的情况是正在使用的库拥有引用。一个小的测试程序将是调试的最佳方式。

于 2012-07-19T16:26:00.920 回答
0

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

于 2012-07-19T18:16:54.583 回答
0

我发现的另一个可能原因是子字符串使用了与原始字符串相同的旧大字符数组。因此,如果您保留子字符串,则会保留完整的字符串。

于 2012-08-04T17:57:54.563 回答