解决大型二进制文件泄漏问题

时间:2017-04-25 14:08:22

标签: memory-management garbage-collection erlang elixir

我有一个elixir / OTP应用程序,由于内存不足问题导致生产崩溃。导致崩溃的功能在专用进程中每6小时调用一次。运行需要几分钟(~30),看起来像这样:

def entry_point do
  get_jobs_to_scrape()
  |> Task.async_stream(&scrape/1)
  |> Stream.map(&persist/1)
  |> Stream.run()
end

在我的本地计算机上,当函数运行时,我看到大型二进制文件内存消耗不断增长:

observer memory usage shows constant memory growth of large binaries

请注意,当我在运行该函数的进程上手动触发垃圾收集时,内存消耗会显着下降,因此对于无法使用GC的几个不同进程来说,它绝对不是问题,但只有一个不会出现问题。 t GC正确。此外,重要的是要说过程 每隔几分钟才能管理GC,但有时候这还不够。生产服务器只有1GB内存,并且在GC启动之前就崩溃了。

尝试解决我遇到的问题Erlang in Anger(参见第66-67页)。一个建议是将所有大型二进制文件操作放在一次性过程中。 scrape函数的返回值是包含大二进制文件的映射。因此,它们在Task.async_stream"工人"之间共享。以及运行该功能的过程。因此,理论上,我可以将persistscrape放在Task.async_stream内。我不想这样做,并通过这个过程保持对persist的调用同步。

另一个建议是定期致电:erlang.garbage_collect。看起来它解决了这个问题,但感觉太过于hacky。作者也不建议这样做。这是我目前的解决方案:

def entry_point do
  my_pid = self()
  Task.async(fn -> periodically_gc(my_pid) end)
  # The rest of the function as before...
end

defp periodically_gc(pid) do
  Process.sleep(30_000)
  if Process.alive?(pid) do
    :erlang.garbage_collect(pid)
    periodically_gc(pid)
  end
end

结果内存负载:

observer memory usage after GC hack

我不太明白书中的其他建议是如何解决这个问题的。

在这种情况下你会推荐什么?保持hacky解决方案或有更好的选择。

1 个答案:

答案 0 :(得分:6)

erlang虚拟机具有垃圾收集机制,默认情况下,该机制针对短期数据进行了优化。一个短暂的进程可能根本不会被垃圾收集,直到它死亡,并且大多数垃圾收集运行只检查新添加的项目。在完全扫描完成之前,不会再次检查在GC运行中幸存的项目。

我建议你尝试调整 fullsweep_after 标志。它可以通过:erlang.system_flag(:fullsweep_after, value)全局设置,也可以使用:erlang.spawn_opt/4为您的特定流程设置。

来自文档:

  

Erlang运行时系统使用分代垃圾收集方案,使用"旧堆"对于至少存在一个垃圾收集的数据。当旧堆上没有空间时,就会完成一次完整的垃圾收集。

     

选项fullsweep_after可以在强制完全扫描之前指定世代集合的最大数量,即使旧堆上有空间也是如此。将数字设置为零会禁用常规收集算法,即在每个垃圾收集中复制所有实时数据。

     

更改fullsweep_after可能有用的几种情况:

     
      
  • 如果不再使用的二进制文件将尽快丢弃。 (将Number设置为零。)
  •   
  • 一个主要拥有短期数据的进程很少或永远不会被填满,也就是说,旧堆主要包含垃圾。要确保偶尔进行完全扫描,请将Number设置为合适的值,例如10或20。
  •   
  • 在RAM数量有限且没有虚拟内存的嵌入式系统中,您可能希望通过将Number设置为零来保留内存。 (可以全局设置该值,请参阅erlang:system_flag / 2.)
  •   

默认值为65535(除非您已通过环境变量ERL_FULLSWEEP_AFTER更改了它),因此任何较低的值都会使垃圾收集更具侵略性。

这是关于这个主题的好读物:https://www.erlang-solutions.com/blog/erlang-19-0-garbage-collector.html