Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

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 つも見逃さないようにしてください。

    C 拡張機能から Ruby メソッドを呼び出す際のメモリ リークを防ぐ

    ユリス・ブオノモ

    私たちのゲスト著者である Ulysse は、元業界の Ruby 開発者であり、ほとんどの時間を世界中を旅することに費やしています。彼は余暇を RGeo と Ruby に費やしており、Ruby の内部をいじるのが大好きです。

    Ulysse Buonomo によるすべての記事


    1. DockerでSinatraアプリをAmazonsEC2ContainerServiceにデプロイする方法

      告白の時間。私の開発環境は、Honeybadgerに取り組んできた4年間あまり変わっていません。しかし、その間、Honeybadgerは、機能する約10のサービスに応じて、はるかに洗練されたものになりました。 そのため、ローカル開発のためにDockerに移行しています。すべての開発者がPostgres、Cassandra、Memcachedなどをセットアップする代わりに、docker-composeを使用して事前に作成された環境を起動できます。素晴らしいです。 当然、Docker化されたアプリケーションをデプロイするのはどれほど簡単かと思い始めました。 Dockerを使用したデプロイ D

    2. Ruby on Rails と Hanami:プロジェクトに適した Web フレームワークの選択

      この投稿は、Rails と Hanami の間のモデルとデータの永続性の違いを明確にするために、2024 年 5 月 23 日に更新されました。 Ruby on Rails は、Ruby エコシステムで最も人気のある Web フレームワークであり、フリーランサーから大手の老舗企業に至るまで、大規模なユーザー ベースを持っています。活発なユーザー コミュニティと幅広いドキュメントを備えているため、単純なアプリケーションから複雑な Web プラットフォームまであらゆるものを構築するために使用できます。 そうは言っても、フルスタック Ruby フレームワークのタイトルをめぐって、新しい参加者が