まさかのメモリリーク?
もはやぼくにとって手放せなくなったmrknさんのPyCall.rbですが、メモリリーク的な現象に遭遇して困ったので逃げ道も含めて情報共有しておきます。
検証コード
まずこちらは何の問題もない Python コードです。ふつうに動き、問題は起きません。
import cv2
for i in range(1000):
img = cv2.imread("foo.jpg")
print(img.shape)
これを PyCall を使って Ruby で書きます。
require "pycall"
cv2 = PyCall.import_module("cv2")
1000 times do
img = cv2.imread("foo.jpg")
puts img.shape
end
するとどうでしょう。なんとメモリがどんどん消費されていくではありませんか!どうもPyCall内で使われたメモリは参照が残るのかGCが処理してくれないようです。img = nil
とか cv2 = nil
とかしても無駄でした。
workaround
で結局どうしたかと言うと格好悪いですがプロセス切り離しました。多少パフォーマンス落ちますが、さすがにここまでやれば大丈夫なようです。
require "pycall"
def quarantine
cv2 = PyCall.import_module("cv2")
img = cv2.imread("foo.jpg")
puts img.shape
end
1000.times do
Process.waitpid(
fork {
quarantine
}
)
end
誰かもっとスマートなやり方見つけたら教えてください。