迭代解析XML文件时严重的内存泄漏

时间:2012-02-10 00:01:11

标签: xml r memory-leaks web-scraping bigdata

上下文

当迭代一组Rdata文件(每个包含HTML代码的字符向量)被加载,分析(通过XML功能)然后再次从内存中删除时,我遇到重要< / strong>增加一个 R过程&#39;内存消耗(最终杀死进程)。

看起来好像是

  • 通过free()
  • 释放对象
  • 通过rm()
  • 删除它们
  • 正在运行gc()

没有任何影响,所以内存消耗累积,直到没有剩余内存。

编辑2012-02-13 23:30:00

感谢包XML的作者和维护者分享的宝贵见解,Duncan Temple Lang(再次:我非常感谢!),问题似乎与外部指针的释放方式密切相关以及如何在XML包中处理垃圾收集。 Duncan发布了一个错误修复版本的软件包(3.92-0),它整合了解析XML和HTML的某些方面,并具有改进的垃圾收集功能,而不再需要通过{显式释放包含外部指针的对象。 {1}}。您可以在Duncan的Omegahat website找到源代码和Windows二进制文件。


编辑2012-02-13 23:34:00

不幸的是,新的软件包版本似乎仍然无法解决我在我所放在一起的小小例子中遇到的问题。我遵循了一些建议并简化了示例,使得更容易掌握并找到出现问题的相关函数(检查函数free()./lib/exampleRun.R)。


编辑2012-02-14 15:00:00

Duncan建议尝试强制通过.lib/scrape.R显式释放已解析的文档。我在示例中添加了一个逻辑交换机(脚本.Call("RS_XML_forceFreeDoc", html)中为do.forcefree),如果设置为./scripts/memory.R,则会执行此操作。不幸的是,这让我的R控制台崩溃了。如果有人可以在他们的机器上验证这一点,那就太棒了!实际上,在使用最新版本的XML时,会自动释放doc 文档(参见上文)。事实上它似乎不是一个错误(根据Duncan的说法)。


编辑2012-02-14 23:12:00

Duncan将另一个版本的XML(3.92-1)推送到他的Omegahat网站Omegahat website。这应该解决一般问题。但是,我似乎对我的例子运气不好,因为我仍然遇到相同的内存泄漏。


编辑2012-02-17 20:39:00&gt;解决方案!

YES!邓肯发现并修复了这个错误!在Windows专用脚本中有点错字,它解释了为什么这个bug在Linux,Mac OS等中没有显示。请查看最新版本3.92-2.!现在,内存消耗与迭代解析和处理XML文件时一样不变!

再次特别感谢Duncan Temple Lang,并感谢回答这个问题的所有人!


&GT;&GT;&GT;原始问题的遗留部分&lt;&lt;&lt;

示例说明(2012-02-14 15:00:00编辑)

  1. 从我的Github repo下载文件夹&#39;记忆&#39;
  2. 第6行打开脚本TRUE并设置a)工作目录,b)示例范围 at 第16行以及c)是否在第22行强制释放已解析的文档。请注意,您仍然可以找到旧脚本;他们被标记为#34;通过&#34; LEGACY &#34;在文件名的末尾。
  3. 运行脚本。
  4. 调查最新文件./scripts/memory.R以查看记录的内存状态随时间的增加情况。我已经包含了两个由我自己的测试运行产生的文本文件。
  5. 我在内存控制方面所做的事情

    • 确保在每次迭代结束时通过./memory_<TIMESTAMP>.txt再次删除加载的对象。
    • 在解析XML文件时,我设置了参数rm(),在通过addFinalizer=TRUE释放C指针并删除包含对象之前,删除了所有引用已解析的XML文档的R对象外部指针。
    • 在这里和那里添加free()
    • 在使用XML包时,尝试遵循Duncan Temple Lang notes关于内存管理的建议(我不得不承认我并不完全理解那里所说的内容)< / LI>

    编辑2012-02-13 23:42:00: 正如我在上面指出的那样,不再需要显式调用gc()后跟free(),因此我对这些调用进行了评论。

    系统信息

    • Windows XP 32位,4 GB RAM
    • Windows 7 32位,2 GB RAM
    • Windows 7 64位,4 GB RAM
    • R 2.14.1
    • XML 3.9-4
    • http://www.omegahat.org/RSXML/
    • 找到的XML 3.92-0

    截至2012-02-09 01:00:00的初步结果

    1. 在几台计算机上运行webscraping场景(请参阅&#34;系统信息&#34;上面),在大约180-350次迭代(取决于操作系统和RAM)后,总是会破坏我的R进程的内存消耗。
    2. 运行普通的rdata方案会产生恒定的内存消耗 当且仅当 在每次迭代中通过rm()设置对垃圾收集器的显式调用时;否则你会遇到与webscraping场景相同的行为。
    3. 问题

      1. 知道导致内存增加的原因是什么?
      2. 如何解决这个问题?
      3. 截止日期为2012-02-013 23:44:00

        在几台计算机上运行gc()中的示例(请参阅&#34;系统信息&#34;上面的部分)在大约180 - 350次迭代后仍会破坏我的R进程的内存消耗(取决于操作系统和内存) )。

        内存消耗量仍然明显增加,即使看到这些数字时看起来可能不那么多,但由于这个原因,我的R进程总会在某个时刻死亡。

        下面,我发布了几个时间序列,这些时间序列来自在具有2 GB RAM的WinXP 32位盒上运行我的示例:

        TS_1(XML 3.9-4,2012-02-09)

        29.07 33.32 30.55 35.32 30.76 30.94 31.13 31.33 35.44 32.34 33.21 32.18 35.46 35.73 35.76 35.68 35.84 35.6 33.49 33.58 33.71 33.82 33.91 34.04 34.15 34.23 37.85 34.68 34.88 35.05 35.2 35.4 35.52 35.66 35.81 35.91 38.08 36.2

        TS_2(XML 3.9-4,2012-02-09)

        28.54 30.13 32.95 30.33 30.43 30.54 35.81 30.99 32.78 31.37 31.56 35.22 31.99 32.22 32.55 32.66 32.84 35.32 33.59 33.32 33.47 33.58 33.69 33.76 33.87 35.5 35.52 34.24 37.67 34.75 34.92 35.1 37.97 35.43 35.57 35.7 38.12 35.98

        与TS_2

        关联的错误消息
        ./scripts/memory.R

        TS_3(XML 3.92-0,2012-02-13)

        20.1 24.14 24.47 22.03 25.21 25.54 23.15 23.5 26.71 24.6 27.39 24.93 28.06 25.64 28.74 26.36 29.3 27.07 30.01 27.77 28.13 31.13 28.84 31.79 29.54 32.4 30.25 33.07 30.96 33.76 31.66 34.4 32.37 35.1 33.07 35.77 38.23 34.16 34.51 34.87 35.22 35.58 35.93 40.54 40.9 41.33 41.6

        与TS_3

        关联的错误消息
        [...]
        Scraping html page 30 of ~/data/rdata/132.rdata
        Scraping html page 31 of ~/data/rdata/132.rdata
        error : Memory allocation failed : growing buffer
        error : Memory allocation failed : growing buffer
        I/O error : write error
        Scraping html page 32 of ~/data/rdata/132.rdata
        Fehler in htmlTreeParse(file = obj[x.html], useInternalNodes = TRUE, addFinalizer =     TRUE): 
         error in creating parser for (null)
        > Synch18832464393836
        

        编辑2012-02-17:请帮我验证计数器值

        如果您可以运行以下代码,那么我帮了大忙。 它不会花费超过2分钟的时间。 您需要做的就是

        1. 下载Rdata file并将其另存为[...] ---------- status: 31.33 % ---------- Scraping html page 1 of 50 Scraping html page 2 of 50 [...] Scraping html page 36 of 50 Scraping html page 37 of 50 Fehler: 1: Memory allocation failed : growing buffer 2: Memory allocation failed : growing buffer
        2. 下载包含scraping function的脚本,并将其另存为seed.Rdata
        3. 相应地设置工作目录后,请输入以下代码。
        4. 代码:

          scrape.R

          我对setwd("set/path/to/your/wd") install.packages("XML", repos="http://www.omegahat.org/R") library(XML) source("scrape.R") load("seed.rdata") html <- htmlParse(obj[1], asText = TRUE) counter.1 <- .Call("R_getXMLRefCount", html) print(counter.1) z <- scrape(html) gc() gc() counter.2 <- .Call("R_getXMLRefCount", html) print(counter.2) rm(html) gc() gc() counter.1的值特别感兴趣,在两次调用中counter.2。实际上,Duncan已经在所有机器上进行了测试。但是,事实证明1在我的所有计算机上都有值counter.2(请参阅上面的详细信息),而这正是造成我问题的原因。

3 个答案:

答案 0 :(得分:7)

XML软件包的网页上,作者Duncan Temple Lang似乎已经广泛地描述了某些内存管理问题。请参阅此页:"Memory Management in the XML Package"

老实说,我不熟悉你的代码和软件包的详细信息,但我想你会在那个页面找到答案,特别是在"Problems"一节中,或者与Duncan Temple Lang直接交流。


更新1. 可能工作的想法是使用multicoreforeach个包(即listResults = foreach(ix = 1:N) %dopar% {your processing;return(listElement)}。我认为对于Windows,你会需要doSMP,或者doRedis;在Linux下,我使用doMC。在任何情况下,通过并行加载,您将获得更快的吞吐量。我认为您可能会得到一些内存使用带来的好处是它可能是分叉R,可能会导致不同的内存清理,因为每个生成的进程在完成时都会被杀死。这不能保证工作,但它可以解决内存和速度问题。

请注意:doSMP有自己的特性(即你可能仍然有一些内存问题)。在SO上还有其他Q&amp; As提到了一些问题,但我还是试一试。

答案 1 :(得分:0)

我在XML包中遇到了类似的问题。 R正在使用的内存量迅速增加,到我的计算机将崩溃的地步。 This answer解决了我的问题,我只设置了addFinalizer = F

这是一个最小的可重现示例:

library(tidyverse)
library(XML)

url <- "https://en.wikipedia.org/wiki/Main_Page"
httr::GET(url) %>% base::saveRDS("html.rds")

在运行其他功能之前的内存使用情况

enter image description here


运行以下命令后的内存使用情况

for(i in 1:10000){
    base::readRDS(file = "html.rds") %>% 
        XML::htmlParse(., asText=TRUE) %>% 
        XML::xpathSApply(., path = "//h1", xmlValue, addFinalizer = F)
}

enter image description here


删除addFinalizer = F(默认)后的内存使用情况

for(i in 1:10000){
    base::readRDS(file = "html.rds") %>% 
        XML::htmlParse(., asText=TRUE) %>% 
        XML::xpathSApply(., path = "//h1", xmlValue)
}

enter image description here

答案 2 :(得分:-2)

@Rappster当我第一次检查并确保XML文档存在然后调用C函数来实现内存时,我的R不会崩溃。

 for (i in 1:1000) {

  pXML<-xmlParse(file)

if(exists("pXML")){
  .Call("RS_XML_forceFreeDoc", pXML)
                  }
}