Errno::ENOMEM: Невозможно выделить память - кошка

У меня есть работа, работающая на производстве, которая обрабатывает xml-файлы. xml насчитывает около 4 КБ и имеет общий размер от 8 до 9 ГБ.

После обработки мы получаем CSV-файлы на выходе. У меня есть команда cat, которая объединит все файлы CSV в один файл, который я получаю:

Errno::ENOMEM: Не удается выделить память

по команде cat (обратная галочка).

Ниже приведены некоторые подробности:

  • Системная память — 4 ГБ
  • Своп - 2 Гб
  • Руби: 1.9.3p286

Файлы обрабатываются с помощью nokogiri и saxbuilder-0.0.8.

Здесь есть блок кода, который будет обрабатывать 4000 XML-файлов, а вывод сохраняется в CSV (по 1 на xml) (извините, я не собираюсь делиться им из-за политики компании).

Ниже приведен код, который объединит выходные файлы в один файл.

Dir["#{processing_directory}/*.csv"].sort_by {|file| [file.count("/"), file]}.each {|file|
            `cat #{file} >> #{final_output_file}`
}

Я сделал снимки потребления памяти во время обработки. Он потребляет почти всю часть памяти, но не выйдет из строя. Это всегда терпит неудачу в команде cat.

Я предполагаю, что при обратной кавычке он пытается разветвить новый процесс, которому не хватает памяти, поэтому он терпит неудачу.

Пожалуйста, дайте мне знать ваше мнение и альтернативу этому.


person Atith    schedule 26.02.2013    source источник
comment
ИМО, было бы разумно показать, что вы делаете.   -  person Dave Newton    schedule 26.02.2013
comment
@DaveNewton Я отредактировал свой пост, спасибо за ваш ответ.   -  person Atith    schedule 26.02.2013
comment
У вас может быть очень мало памяти, чтобы это произошло, вы уверены, что у вас достаточно памяти? Каков результат free -m ?   -  person Intrepidd    schedule 26.02.2013
comment
@Intrepidd, я согласен с вами, free -m показывает, что после обработки около 3000 файлов почти не осталось 150 МБ. Но он продолжает обрабатывать все файлы и терпит неудачу только по команде cat.   -  person Atith    schedule 26.02.2013
comment
Это нормально, у вас достаточно памяти для перечисления всех файлов и создания шелла, но созданный шелл не работает при запуске cat, я напишу для вас решение и опубликую его как ответ   -  person Intrepidd    schedule 26.02.2013
comment
@Intrepidd, это было бы здорово, спасибо :)   -  person Atith    schedule 26.02.2013
comment
Обратитесь к следующему вопросу и взгляните на принятый ответ. Это отличное объяснение того, что на самом деле происходит. stackoverflow.com/questions/28286002/   -  person Sharuzzaman Ahmat Raslan    schedule 12.08.2017


Ответы (3)


Таким образом, кажется, что ваша система использует довольно мало памяти, и порождение оболочки + вызов кота слишком много для оставшейся памяти.

Если вы не возражаете против потери скорости, вы можете объединить файлы в ruby ​​​​с небольшими буферами. Это позволяет избежать появления оболочки, и вы можете контролировать размер буфера.

Это не проверено, но вы поняли:

buffer_size = 4096
output_file = File.open(final_output_file, 'w')

Dir["#{processing_directory}/*.csv"].sort_by {|file| [file.count("/"), file]}.each do |file|
  f = File.open(file)
  while buffer = f.read(buffer_size)
    output_file.write(buffer)
  end
  f.close
end
person Intrepidd    schedule 26.02.2013
comment
Да, это может сработать, я попробую и дам вам знать. И знаете ли вы какие-либо проблемы Nokogiri на Ruby 1.9.3, связанные с памятью? Недавно мы обновили ruby ​​с 1.9.2 до 1.9.3, у меня такое ощущение, что это тоже может быть причиной. - person Atith; 26.02.2013

Вероятно, у вас закончилась физическая память, поэтому дважды проверьте это и проверьте свой своп (free -m). Если у вас нет области подкачки, создайте ее.

В противном случае, если с вашей памятью все в порядке, ошибка, скорее всего, вызвана ограничениями ресурсов оболочки. Вы можете проверить их до ulimit -a.

Их можно изменить с помощью ulimit, который может изменить ограничения ресурсов оболочки (см.: help ulimit), например.

ulimit -Sn unlimited && ulimit -Sl unlimited

Чтобы сделать эти ограничения постоянными, вы можете настроить их, создав файл настроек ulimit с помощью следующей команды оболочки:

cat | sudo tee /etc/security/limits.d/01-${USER}.conf <<EOF
${USER} soft core unlimited
${USER} soft fsize unlimited
${USER} soft nofile 4096
${USER} soft nproc 30654
EOF

Или используйте /etc/sysctl.conf для глобального изменения ограничения (man sysctl.conf), например.

kern.maxprocperuid=1000
kern.maxproc=2000
kern.maxfilesperproc=20000
kern.maxfiles=50000
person kenorb    schedule 10.07.2016

У меня такая же проблема, только вместо cat было sendmail (gem mail).

Я нашел проблему и решение здесь установив posix-spawn gem, например.

gem install posix-spawn

и вот пример:

a = (1..500_000_000).to_a

require 'posix/spawn'
POSIX::Spawn::spawn('ls')

На этот раз создание дочернего процесса должно завершиться успешно.

См. также: Минимизация использования памяти для создания подпроцессов приложения по адресу Оракул.

person unixs    schedule 04.04.2015