【发布时间】:2017-04-17 22:11:23
【问题描述】:
我一定遗漏了一些东西,但我用 Ruby 编写的每个应用程序似乎都在泄漏一些内存。我使用 Ruby MRI 2.3,但我发现其他版本的行为相同。
每当我编写一个在循环中执行某些操作的测试应用程序时,它都会慢慢泄漏内存。
while true
#do something
sleep 0.1
end
例如,我可以写入数组,然后循环清理它,或者只是发送 http post 请求。
这只是一个例子,但我有很多这样的例子:
require 'net/http'
require 'json'
require 'openssl'
class Tester
def send_http some_json
begin
@uri = URI('SERVER_URL')
@http = Net::HTTP.new(@uri.host, @uri.port)
@http.use_ssl = true
@http.keep_alive_timeout = 10
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
@http.read_timeout = 30
@req = Net::HTTP::Post.new(@uri.path, 'Content-Type' => 'application/json')
@req.body = some_json.to_json
res = @http.request(@req)
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end
end
def run
while true
some_json = {"name": "My name"}
send_http(some_json)
sleep 0.1
end
end
end
Tester.new.run
我看到的泄漏非常小,每小时可以有 0.5 mb。
我使用 MemoryProfiler 和 GC::Profiler.enable 运行了代码,它表明我没有泄漏。所以它必须是2个选项:
C 代码中存在内存泄漏。这可能是可能的,但我没有使用任何外部 gem,所以我很难相信 Ruby 正在泄漏。
没有内存泄漏,这是某种 Ruby 内存管理机制。问题是我可以目不转睛地看到记忆在增长。能长到什么时候?我需要等待多久才能知道是泄漏还是现在?
同样的代码在 JRuby 上运行得非常好,没有任何泄漏。
我很惊讶地看到一个帖子:
stack overlflow 来自乔·埃德加:
Ruby 的历史主要是作为文本处理的命令行工具 因此它重视快速启动和较小的内存占用。它 不是为长时间运行的守护进程/服务器进程设计的
如果那里写的是真的,并且 Ruby 没有将内存释放回操作系统,那么......我们总会有泄漏,对吧?
例如:
- Ruby 向操作系统请求内存。
- 操作系统为 Ruby 提供内存。
- Ruby 释放了内存,但 GC 仍然没有运行。
- Ruby 向操作系统请求更多内存。
- 操作系统为 Ruby 提供更多内存。
- Ruby 运行 GC,但为时已晚,因为 Ruby 已经询问了两次。
- 等等等等。
我在这里错过了什么?
【问题讨论】:
-
您确定旧变量会超出范围吗?当 GC 收集垃圾时,旧变量似乎仍会被标记为“正在使用”。确保旧变量在测试时超出范围。考虑将循环操作嵌套在函数中 (
while true; sleep 0.1; run_action; end)。 -
在您的示例中,JSON 哈希中的临时字符串可能会保留在内存中,直到循环结束......但是您每次迭代都会创建新字符串。尝试将
some_json = {"name".freeze: "My name".freeze}放在循环之前。 -
你是对的,我每次都创建新字符串,但为什么会出现问题?我希望 GC 收集它们。它应该不会导致内存泄漏,对吧?
-
字符串是在循环范围内创建的。有可能(只是我的 2 美分)GC 只会在程序退出循环范围后才收集这些字符串......这是永远不会的。
-
你有什么运气吗?
标签: ruby memory-leaks