我正在使用Merb开发Web应用程序,我正在寻找一些安全稳定的图像处理库。我以前在php中使用Imagick,然后转移到ruby并开始使用RMagick。但有一个问题。长时间运行的脚本导致内存泄漏。有几种解决方案存在,但我不知道哪一种最稳定。那么,您怎么看?
现在,我的应用程序使用我编写的内部API来处理PHP中的图像。它与其他应用程序一起运行在单独的服务器上,因此它不是一个大问题。但我认为它不是一个好的架构。
无论如何,我会考虑任何实用技巧。
答案 0 :(得分:11)
我也遇到过这个问题 - 解决方法是强制进行垃圾回收。
将图像变量重新分配给新图像后,只需使用GC.start确保从内存中释放旧参考。
在RMagick的后期版本中,我也相信你也可以召唤毁灭!在完成处理后的图像上。
这两者的结合可能会确保你被覆盖,但我不确定现实生活对性能的影响(我认为在大多数情况下它可以忽略不计)。
或者,您可以使用mini-magick,它是ImageMagick命令行客户端的包装器。
答案 1 :(得分:7)
使用RMagick时,重要的是要记住在完成后销毁图像,否则在处理大量图像时会填充/ tmp目录。例如,你必须调用destroy!
require 'RMagick'
Dir.foreach('/home/tiffs/') do |file|
next if file == '.' or file == '..'
image = Magick::Image.read(file).first
image.format = "PNG"
image.write("/home/png/#{File.basename(file, '.*')}.png")
image.destroy!
end
答案 2 :(得分:5)
实际上,它并不是真正的Ruby特定问题,其他口译员也同样如此。具体问题是Ruby的GC只看到由Ruby本身分配的内存,而不是外部库(除了使用Rubys内存管理工具的库的明显例外)。因此,Ruby内存空间中的ImageMagick-Object非常小,但ImageMagick管理的空间中的图像很大。所以,这本身并不是泄漏,但它的行为就像一个。 如果您的进程保持在一定限度(标准为8MB),Rubys垃圾收集器永远不会启动。由于ImageMagick从不在Ruby空间中创建大型对象,因此它可能永远不会开始使用。因此,要么使用提出的生成新进程的方法,要么使用exec。另一个相当漂亮的是在后端有一个图像处理服务,用于执行每项任务。另一个是进行某种监控,每隔一段时间启动一次GC。
Timothy Paul Hunter(RMagick的作者)还有另一个名为MagickWand的图书馆试图解决这些问题并创建一个更好的API。它是alpha版本,但需要一个相当新的ImageMagick版本。
答案 3 :(得分:0)
现在您可以告诉ImageMagick应该使用哪个内存空间。
我认为RMAGICK_ENABLE_MANAGED_MEMORY = true
和GC.start
是您所需要的。
MANAGED_MEMORY
If true, RMagick is using Ruby managed memory for all allocations. If false,
RMagick allocates memory for objects directly from the operating system. You can
enable RMagick to use Ruby managed memory (when built with ImageMagick 6.4.0-11
and later) by setting
RMAGICK_ENABLE_MANAGED_MEMORY = true
before requiring RMagick.
https://rmagick.github.io/constants.html
但是,image.destroy!
本身足以稳定内存消耗。
答案 4 :(得分:-1)
这不是由ImageMagick引起的;它归功于Ruby本身,这是一个众所周知的问题。我的建议是将你的程序分成两部分:一个长期运行的部分,它只分配很少的内存,只处理系统的控制,还有一个单独的程序来实际处理工作。长时间运行的控制过程应该足以找到它产生的子进程的一些工作,并且子进程应该对该特定工作项执行所有处理。
另一种选择是将两者结合起来,但在工作单元完成后,使用exec
将您的流程替换为同一程序的新启动版本,该程序将搜索另一个工作项,流程它,并再次执行。
这假设工作项目相当大,如果您使用ImageMagick,它们几乎肯定是。如果它们不是,你会发现产生一个新进程并让Ruby解释器重新解析整个程序的开销会开始变得有点过大。您可以通过让程序在重新执行之前执行更多工作单元(例如,十个或一百个)来处理此问题。