Help us understand the problem. What is going on with this article?

PyCall.rb でメモリリークみたいな現象が起こったので workaround

まさかのメモリリーク?

もはやぼくにとって手放せなくなった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

誰かもっとスマートなやり方見つけたら教えてください。

yagshi
PGP fingerprint: 7062 E716 FAAE 14EC 3A0A 45A0 FA71 C547 135A C420
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away