2016-05-04 5 views
4

私はPythonのctypesモジュールを使用してライブラリをロードすることで、ビジネスソフトウェアのベンダーのC APIを使用しています。Pythonで使用されているベンダーのDLLでメモリリークを回避するには?

私が書いたソフトウェアをデプロイした後、APIの一部である特定の関数の呼び出し回数に応じて、ベンダーのライブラリが一貫した予測可能な基準でメモリをリークすることがわかりました。

ヒープ割り当てを使用しないCプログラムでもリークが重複していました。

私はこの問題についてベンダーに連絡し、彼らはそれに取り組んでいると言いましたが、次のバージョンのソフトウェアまで現実的には修正を期待できません。

私はリーク関数の呼び出しの特定のしきい値の後、ベンダーのdllをリロードする考えがありましたが、これは漏れたメモリを解放しませんでした。

が、私はそうのようにアンロードするライブラリを強制することが分かっ:

_ctypes.FreeLibrary(vendor_dll._handle) 

これは、メモリを解放しますが、ベンダーのAPIを使用しての分の数の後に、一見ランダムにクラッシュするインタプリタの原因となります。

私は私の状況を説明し、Pythonのバグトラッカーでこの問題を発見した: https://bugs.python.org/issue14597

ライブラリへのオープン参照が残っていた場合には、アンロードすることを強制することは必然的にPythonインタプリタをクラッシュするようです。

最悪のシナリオでは、ベンダーのライブラリを別のプロセスで読み込み、マルチプロセッシングキューを使用してプロキシ要求を行い、インタープリタが終了した場合にプロセスを再構築するためにウォッチドッグをセットアップできます。

これを回避するより良い方法はありますか?

+0

上記の回避策のより洗練された解決策は、定期的な間隔でワーカープロセスを終了して再起動することです。これにより、ベンダーのライブラリによって漏洩されたメモリは解放されますが、インタプリタはクラッシュしません。 – jakogut

+0

ライブラリを解放すると、実際にはメモリリークではありません。ライブラリは 'DLL_PROCESS_DETACH'で解放するためにこれらの割り当てを追跡しなければなりません。 'vendor_dll'をアンロードするには、まず現在のインスタンスへの参照をすべて削除し、未処理の非同期コールバックを含め、コードを再度実行しないようにしてください。その後、DLLをアンロードして再ロードすることができます。共有ライブラリは参照カウントされるので、実際にアンロードされるようにするには 'OSError'が発生するまで' FreeLibrary'を呼び出します。 – eryksun

答えて

1

最後に、私はそうのように、別のプロセスで、ベンダーのライブラリをロードし、そしてPyro4からアクセスし、問題を修正:

class LibraryWorker(multiprocessing.Process): 
    def __init__(self): 
     super().__init__() 

    def run(self): 
     self.library = ctypes.windll.LoadLibrary(
      'vendor_library.dll') 

     Pyro4.serveSimple(
      {self, 'library'}, 
      ns=False) 

    def lib_func(self): 
     res = self.library.func() 
     return res 

古いコードをマッサージするために余分な作業のビットでした2つのプロセス間でctypesポインタを渡さないようにしますが、それは機能します。

ライブラリを別のプロセスで読み込むと、メモリ使用量を記録できます。それが高すぎると、メモリを解放するためにプロセスを終了して再作成することができます。

関連する問題