Errno::ENOMEM: 无法分配内存 - cat

Posted

技术标签:

【中文标题】Errno::ENOMEM: 无法分配内存 - cat【英文标题】:Errno::ENOMEM: Cannot allocate memory - cat 【发布时间】:2013-02-11 17:28:06 【问题描述】:

我有一个工作在生产中运行,它处理 xml 文件。 xml 文件总计约 4k,大小为 8 到 9 GB。

处理后,我们得到 CSV 文件作为输出。我有一个 cat 命令,它将所有 CSV 文件合并到我得到的单个文件中:

Errno::ENOMEM: 无法分配内存

cat(反引号)命令上。

以下是一些细节:

系统内存 - 4 GB 交换 - 2 GB 红宝石:1.9.3p286

使用nokogirisaxbuilder-0.0.8 处理文件。

这里有一段代码可以处理 4,000 个 XML 文件,并且输出保存为 CSV(每个 xml 1 个)(抱歉,我不打算根据公司政策分享它)。

以下是将输出文件合并为单个文件的代码

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

我在处理过程中拍摄了内存消耗快照。它几乎消耗了所有部分内存,但是,它不会失败。 它总是在 cat 命令上失败。

我猜,在反引号时,它会尝试派生一个没有获得足够内存的新进程,因此它失败了。

请让我知道您的意见和替代方案。

【问题讨论】:

IMO 实际展示你在做什么是有意义的。 @DaveNewton 我已经编辑了我的帖子,感谢您的回复 您的内存可能非常低,导致这种情况发生,您确定还有足够的内存吗? free -m 的输出是什么? @Intrepidd,我同意你的观点,free -m 在处理大约 3,000 个文件后显示几乎没有 150 MB。但是,它会继续处理所有文件,并且只会在 cat 命令上失败。 这很正常,你有足够的内存来列出所有文件并生成一个shell,但是运行cat时生成的shell会失败,我会为你写一个解决方案并发布它作为答案 【参考方案1】:

因此,您的系统似乎在内存上运行得非常低,并且生成一个 shell + 调用 cat 对于剩余的少量内存来说太多了。

如果您不介意失去一些速度,您可以在 ruby​​ 中合并文件,使用小缓冲区。 这样可以避免生成 shell,并且您可以控制缓冲区大小。

这是未经测试的,但你明白了:

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

【讨论】:

是的,这可能有效,我会试试这个并告诉你。而且,您知道 Ruby 1.9.3 上的 Nokogiri 与内存相关的任何问题吗?我们最近将 ruby​​ 从 1.9.2 升级到 1.9.3,我感觉这也可能是一个原因。【参考方案2】:

我有同样的问题,但不是cat,而是sendmail (gem mail)。

我通过安装posix-spawn gem 找到了问题和解决方案here,例如

gem install posix-spawn

这是一个例子:

a = (1..500_000_000).to_a

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

这次创建子进程应该会成功。

另请参阅:Minimizing Memory Usage for Creating Application Subprocesses 在 Oracle。

【讨论】:

【参考方案3】:

您的物理内存可能已用完,因此请仔细检查并验证您的交换 (free -m)。如果您没有交换空间,create one。

否则如果你的记忆力没问题,这个错误很可能是shell资源限制造成的。您可以通过ulimit -a查看。

它们可以通过ulimit 更改,它可以修改shell 资源限制(参见:help ulimit),例如

ulimit -Sn unlimited && ulimit -Sl unlimited

要使这些限制持久化,您可以通过以下 shell 命令创建 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

【讨论】:

以上是关于Errno::ENOMEM: 无法分配内存 - cat的主要内容,如果未能解决你的问题,请参考以下文章

gitlab 无法查看提交的文件Errno::ENOMEM (Cannot allocate memory - /opt/gitlab/embedded/bin/git):

mmap:无法分配内存

虚拟内存无法设置的问题,谁来解决一下?

Ubuntu 增加交换空间

使用system(命令)时,AWS中的C ++代码失败并显示错误“无法分配内存”

C语言动态内存