Python 3.12.7 で msgpack におけるメモリリークが修正された

github.com

Python 3.12から参照カウントを固定化するImmortal Objectが導入されたのですが、それがinterned string全てを強制的に固定参照カウントにしてしまっていました。

Pythonの文字列はimmutableなので、短くて大量に同じ値が出てくる可能性がある文字列は積極的にオブジェクトを1つにまとめて省メモリ化したいです。 さらにその文字列が変数名などと共通する可能性があるときは、インターン化することで単にインスタンスを一つにまとめることによる省メモリ効果だけでなく、その文字列をキーにして名前を参照するときに文字列比較がポインタ比較だけで終わるという効果も期待できます。

ということで、Pythonpathlibの一部sys.intern() が使われていたり、私が開発しているmsgpack-pythonでもオブジェクトのキーに対してインターンを実施していました。 それがインターン文字列のimmortal化の影響で、pathlibでランダムなディレクトリ名(ハッシュ値ディレクトリ名になってるとか)を扱ったり、外部から入力されるmsgpackのキーが固定の文字列ではなく多様な文字列を含んでいる場合などに、緩やかにメモリ使用量が増え続けることになってしまっていました。

それがrevertされてintern文字列全てがimmortalにならなくなったことで、Python 3.12.7で問題が解消されました。該当するユースケースを持つアプリは早めにアップデートをお勧めします。

他に影響を受けそうなモジュールとして標準ライブラリのjsonモジュールとorjsonを見てみたのですが、jsonインターン化せず独自のdictを使ってオブジェクト再利用を行なっていて、orjsonもassociative_cacheを使った再利用を行なっているので、インターン文字列永続化の影響はありませんでした。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。