C 拡張機能から Ruby メソッドを呼び出す際のメモリ リークを防ぐ
メモリリークは gem ユーザーにとって厄介です。これらは追跡が難しく、高額なインフラストラクチャ コストにつながる可能性があります。
C 拡張機能内のメモリ リークはさらに深刻です。 Ruby のリークの発見に関するツールや記事がたくさんあります。ただし、C の内部への同じアクセス権はありません。
rb_funcall の単純な使用法 メモリ リークを引き起こす可能性があります。rb_protect を使用することをお勧めします。 代わりに。したがって、C 拡張ライターの場合は、Gem を使用する開発者のために読み進めてください。
始めましょう!
rb_funcall の問題 そしてC
rb_funcall Ruby とライブラリの C 部分の間で対話する必要があるが、C を少し書くだけで済む場合に、このツールは優れたツールになります。
ただし、rb_funcall を実行すると、 , あなたはもはや、すべてが単純な C 言語ではありません。呼び出された関数が次の場合、泥水の中に放置される可能性があります。
1番が一番捕まえやすいです。最終的にセグメンテーション違反が発生する可能性が高く、テスト スイートが十分に完成している場合は、公開する前にそれを検出する必要があります。
ただし、後者はメモリ リークを引き起こし、コードベースを非常に読みにくくする可能性があります。では、それを見てみましょう。
Ruby での発生により C メモリ リークが発生する
Ruby の発生メカニズムは、1 つのスコープからエラーをキャッチした最初の親までコードの部分間をジャンプします。これは、longjmp を使用して MRI に実装されます。 と setjmp .
これがどのように構築されるかに興味がある場合は、『Ruby Hacking Guide』の「Evaluator」の章を読んでください。一言で言えば、begin..ensure を使用する場合 ブロックすると、setjmp() 、このブロック内でレイズすると、longjmp() になります。 保存された位置に移動します。
したがって、関数が rb_funcall で呼び出された場合 、実行されなかった後に呼び出される C コード。
以下の例は、潜在的なリークを示しています。 json_parse の場合 上げるとリークします。
もちろん、上の例は少しばかげています。解放部分と Ruby 処理部分を逆にすることもできます。ただし、これは常に可能であるとは限りません。関数本体が長くなると、より複雑に絡み合う可能性があります。
begin..ensure の使用 Ruby で
Ruby を使用している場合は、代わりに begin..ensure を使用して上記の例を書くこともできます。 :
この API は、rb_rescue を使用して C でも使用できます。 および rb_ensure :
ただし、これは少し面倒です。rescue を追加したい場合は、 パーティーにブロックすると、読みにくくなります。 begin..rescue..ensure..end を使用したい場合は、Peter Zhu の「A Rubyist's Walk Among the C-side (Part 8):Exceptions &Error Handling」を読むことをお勧めします。 C の API。
rb_protect の使用 C の場合
別のオプションもあります。まず、Ruby でどのように見えるかを見てみましょう。
これは Ruby では奇妙に見えますが、C には非常に適したワークフローです。MRI にはそのための API、rb_protect があります。 、C 関数は次のようになります。
上記のメソッドは、freedeverything を取得した後に Ruby エラーを再発生させます。
空の rescue を使用してエラーを無視することもできることに注意してください。 Ruby のブロック:
警告: エラーが発生しない場合、rb_set_errinfo(Qnil) このステップは重要であり、ユーザーが知る必要のないエラーに関する情報を入手できないようにする必要があります。
または、条件付きで rescue My::Error などのエラーを発生させることを選択できます。 :
実際には rb_errinfo() を検討してください。 $! と同じ グローバル変数。
これはすべて素晴らしいことですが、要約すると、rb_funcall が 1 つになります。 その API を簡素化できるだけです。
rb_protect の使用の背後にある全体的な考え方 関数がある場合の API を上げるのは、可読性を高めるためです。関数が発生できるかどうかを確認する必要はありません。発生できると想定し、状態を使用してそれを処理します。
rb_protect_funcall 提案
rb_funcall を分離しましょう 、 それが唯一の危険であるためです。 使用する方法。これを行う API は次のとおりです。
この API は rb_funcall と同じです 、state を使用 rb_protectから 。したがって、使用法は非常に簡単です。
この API は Ruby ではまだ利用できず、今後も利用できない可能性があります。 RGeo (MIT LICENSE) から取得できます。
実際の例
実際の例を見たい場合は、RGeocodebase を読むことをお勧めします。最近、完全な rb_protect に移行したためです。 。 rgeo_convert_to_geos_geometry などの関数もあります。 、使用法を簡単にするためにこの状態を伝播します。この関数は、調査を開始するのに適した場所です。
私たちが行った選択についてさらに話し合うために、お気軽に RGeo で問題を開いてください。
まとめ
この投稿では、rb_funcall を使用しないよう警告しました。 メモリリークを引き起こす可能性があるため、C では使用しないでください。 begin..ensure を使用して調査しました。 または rb_protect 代わりに。
コーディングを楽しんでください!
追記Ruby Magic の投稿を報道後すぐに読みたい場合は、Ruby Magic ニュースレターを購読して、投稿を 1 つも見逃さないようにしてください。
ユリス・ブオノモ
私たちのゲスト著者である Ulysse は、元業界の Ruby 開発者であり、ほとんどの時間を世界中を旅することに費やしています。彼は余暇を RGeo と Ruby に費やしており、Ruby の内部をいじるのが大好きです。
Ulysse Buonomo によるすべての記事
-
DockerでSinatraアプリをAmazonsEC2ContainerServiceにデプロイする方法
告白の時間。私の開発環境は、Honeybadgerに取り組んできた4年間あまり変わっていません。しかし、その間、Honeybadgerは、機能する約10のサービスに応じて、はるかに洗練されたものになりました。 そのため、ローカル開発のためにDockerに移行しています。すべての開発者がPostgres、Cassandra、Memcachedなどをセットアップする代わりに、docker-composeを使用して事前に作成された環境を起動できます。素晴らしいです。 当然、Docker化されたアプリケーションをデプロイするのはどれほど簡単かと思い始めました。 Dockerを使用したデプロイ D
-
Ruby on Rails と Hanami:プロジェクトに適した Web フレームワークの選択
この投稿は、Rails と Hanami の間のモデルとデータの永続性の違いを明確にするために、2024 年 5 月 23 日に更新されました。 Ruby on Rails は、Ruby エコシステムで最も人気のある Web フレームワークであり、フリーランサーから大手の老舗企業に至るまで、大規模なユーザー ベースを持っています。活発なユーザー コミュニティと幅広いドキュメントを備えているため、単純なアプリケーションから複雑な Web プラットフォームまであらゆるものを構築するために使用できます。 そうは言っても、フルスタック Ruby フレームワークのタイトルをめぐって、新しい参加者が