Серьезная утечка памяти при итеративном разборе файлов XML - PullRequest
14 голосов
/ 10 февраля 2012

Контекст

При переборе набора файлов Rdata (каждый из которых содержит вектор символов HTML-кода), которые загружаются, анализируются (с помощью функциональных возможностей XML ) и затем снова удаляются из памяти, Я испытываю значительное увеличение R процесса 'потребление памяти (убивая процесс в конце концов).

Это просто похоже на

  • освобождение объектов через free(),
  • удаление их через rm() и
  • работает gc()

не имеет никаких эффектов, поэтому потребление памяти накапливается до тех пор, пока не останется больше памяти.

РЕДАКТИРОВАТЬ 2012-02-13 23: 30: 00

Благодаря ценному пониманию, предоставленному автором и сопровождающим пакета XML , Дункан Темпл Ланг (снова: я очень ценю это!), Проблема, кажется, тесно связанные со способом освобождения внешних указателей и обработкой сборки мусора в пакете XML. Дункан выпустил исправленную версию пакета (3.92-0), в которой объединены некоторые аспекты синтаксического анализа XML и HTML и реализована улучшенная сборка мусора, в которой больше нет необходимости явно освобождать объект, содержащий внешний указатель, через free(). Вы можете найти исходный код и двоичный файл Windows на веб-сайте Дункана Omegahat .


РЕДАКТИРОВАТЬ 2012-02-13 23: 34: 00

К сожалению, новая версия пакета, похоже, все еще не решает проблемы, с которыми я сталкиваюсь в небольшом небольшом примере, который я собрал. Я последовал некоторому предложению и немного упростил пример, чтобы было легче понять и найти соответствующие функции там, где кажется, что что-то идет не так (проверьте функции ./lib/exampleRun.R и .lib/scrape.R).


РЕДАКТИРОВАТЬ 2012-02-14 15: 00: 00

Дункан предложил попытаться принудительно освободить разобранный документ явно через .Call("RS_XML_forceFreeDoc", html). Я включил в пример логический переключатель (do.forcefree в скрипте ./scripts/memory.R), который, если установить значение TRUE, сделает именно это. К сожалению, это привело к краху моей консоли R. Было бы здорово, если бы кто-то мог проверить это на своей машине! На самом деле, документ должен автоматически освобождаться при использовании последней версии XML (см. Выше). Тот факт, что это не является ошибкой (согласно Дункану).


РЕДАКТИРОВАТЬ 2012-02-14 23: 12: 00

Дункан отправил еще одну версию XML (3.92-1) на свой веб-сайт Omegahat Веб-сайт Omegahat . Это должно решить проблему в целом. Однако мне, кажется, не повезло с моим примером, поскольку я все еще испытываю ту же утечку памяти.


РЕДАКТИРОВАТЬ 2012-02-17 20:39:00> РЕШЕНИЕ!

ДА! Дункан нашел и исправил ошибку! Это была небольшая опечатка в сценарии только для Windows, которая объясняла, почему ошибка не отображалась в Linux, Mac OS и т. Д. Проверьте последнюю версию 3.92-2. ! Потребление памяти стало таким же постоянным, как и при итеративном анализе и обработке файлов XML!

Еще раз отдельное спасибо Duncan Temple Lang и всем остальным, кто ответил на этот вопрос!


>>> ЧАСТИ НАСЛЕДИЯ ОРИГИНАЛЬНОГО ВОПРОСА <<< </h2> Пример инструкции (отредактировано 2012-02-14 15:00:00)

  1. Загрузить папку 'memory' из моего Github-репо .
  2. Откройте скрипт ./scripts/memory.R и установите а) свой рабочий каталог в строке 6 , б) примерную область в строке 16 также c) принудительно ли освобождать проанализированный документ или нет в строка 22 * ​​1095 *. Обратите внимание, что вы все еще можете найти старые скрипты; они помечены " LEGACY " в конце имени файла.
  3. Запустите скрипт.
  4. Исследуйте последний файл ./memory_<TIMESTAMP>.txt, чтобы увидеть увеличение зарегистрированных состояний памяти с течением времени. Я включил два текстовых файла, которые были получены в результате моих собственных тестов.

То, что я сделал в отношении управления памятью

  • убедиться, что загруженный объект снова удаляется с помощью rm() в конце каждой итерации.
  • При синтаксическом анализе XML-файлов я установил аргумент addFinalizer=TRUE, удалил все объекты R, которые имеют ссылку на проанализированный XML-документ, прежде чем освободить указатель C с помощью free() и удалить объект, содержащий внешний указатель.
  • добавление gc() здесь и там.
  • пытается следовать советам Данкан Темпл Ланга отмечает по управлению памятью при использовании его пакета XML (я должен признать, что я не полностью понял, что там сказано)

РЕДАКТИРОВАТЬ 2012-02-13 23: 42: 00: Как я указывал выше, явные вызовы free(), за которыми следует rm(), больше не нужны, поэтому я прокомментировал эти вызовы.

Информация о системе

  • Windows XP 32 бит, 4 ГБ ОЗУ
  • Windows 7 32 бит, 2 ГБ ОЗУ
  • Windows 7 64 бит, 4 ГБ ОЗУ
  • R 2.14.1
  • XML 3,9-4
  • XML 3,92-0, как указано в http://www.omegahat.org/RSXML/

Исходные данные на 2012-02-09 01: 00: 00

  1. Запуск сценария webscraping на нескольких машинах (см. Раздел «Информация о системе» выше) всегда приводит к перегрузке памяти моего процесса R после примерно 180 - 350 итераций (в зависимости от ОС и ОЗУ).
  2. Запуск сценария простого rdata приводит к постоянному потреблению памяти тогда и только тогда, когда вы устанавливаете явный вызов сборщика мусора через gc() в каждой итерации; иначе вы испытываете то же поведение, что и в сценарии веб-скрепинга.

Вопросы

  1. Есть идеи, что вызывает увеличение памяти?
  2. Есть идеи, как обойти это?

Результаты по состоянию на 2012-02-013 23: 44: 00

Выполнение примера в ./scripts/memory.R на нескольких машинах (см. Раздел «Сведения о системе» выше) по-прежнему приводит к увеличению потребления памяти моим процессом R после примерно 180 - 350 итераций (в зависимости от ОС и ОЗУ).

Наблюдается очевидное увеличение потребления памяти, и, хотя, глядя на цифры, оно может показаться не таким уж большим, мои R-процессы всегда в какой-то момент прекращаются из-за этого.

Ниже я опубликовал несколько временных рядов, которые явились результатом запуска моего примера на 32-битной коробке WinXP с 2 ГБ ОЗУ:

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

[...]
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

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

[...]
---------- 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

Редактировать 2012-02-17: пожалуйста, помогите мне проверить значение счетчика

Вы бы оказали мне огромную услугу, если бы могли запустить следующий код. Это не займет больше 2 минут вашего времени . Все, что вам нужно сделать, это

  1. Загрузите файл Rdata и сохраните его как seed.Rdata.
  2. Загрузите скрипт, содержащий мою функцию очистки и сохраните его как scrape.R.
  3. Исходный код после соответствующей настройки рабочего каталога.

Код:

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, которые должны быть 1 в обоих вызовах.Фактически, именно на всех машинах Duncan проверил это.Однако, как оказалось, counter.2 имеет значение 259 на всех моих машинах (подробности см. Выше), и это именно то, что вызывает мою проблему.

Ответы [ 2 ]

7 голосов
/ 10 февраля 2012

С веб-страницы пакета XML кажется, что автор, Duncan Temple Lang, довольно подробно описал некоторые проблемы управления памятью. См. Эту страницу: «Управление памятью в пакете XML» .

Честно говоря, я не разбираюсь в деталях того, что здесь происходит с вашим кодом и пакетом, но я думаю, что вы либо найдете ответ на этой странице, в частности, в разделе "Проблемы" , или в прямой связи с Дунканом Темпл Ланг.


Обновление 1. Идея, что может работать, заключается в использовании пакетов multicore и foreach (т.е. listResults = foreach(ix = 1:N) %dopar% {your processing;return(listElement)}. Я думаю, что для Windows вам понадобится doSMP, или, может быть, doRedis; в Linux я использую doMC. В любом случае, распараллеливая загрузку, вы получите более высокую пропускную способность. Я думаю, что вы можете получить некоторую выгоду от использования памяти в том, что это может быть разветвление R, может привести к различной очистке памяти, так как каждый порожденный процесс будет завершен после завершения. Это не гарантированно сработает, но может решить проблемы как с памятью, так и со скоростью.

Обратите внимание: хотя doSMP имеет свои особенности (т. Е. У вас все еще могут быть проблемы с памятью). В SO были и другие вопросы и ответы, в которых упоминались некоторые проблемы, но я бы все-таки попробовал.

0 голосов
/ 21 июня 2016

@ Rappster My R не падает, когда я сначала проверяю и проверяю наличие документа XML, а затем вызываю функцию C для реализации памяти.

 for (i in 1:1000) {

  pXML<-xmlParse(file)

if(exists("pXML")){
  .Call("RS_XML_forceFreeDoc", pXML)
                  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...