ホームページ  >  記事  >  バックエンド開発  >  Python のメモリ管理の仕組みを理解していますか?

Python のメモリ管理の仕組みを理解していますか?

WBOY
WBOY転載
2023-04-12 16:25:091194ブラウズ

Python は開発者に多くの利便性を提供します。その最大の利点の 1 つは、実質的に心配のないメモリ管理です。開発者は、Python でオブジェクトやデータ構造のメモリを手動で割り当て、追跡、解放する必要がなくなりました。ランタイムがこれらの作業をすべて実行してくれるため、ユーザーはマシンレベルの詳細を議論するのではなく、実際の問題の解決に集中できます。

Python のメモリ管理の仕組みを理解していますか?

# それでも、経験の浅い Python ユーザーであっても、Python のガベージ コレクションとメモリ管理がどのように機能するかを理解することは有益です。これらのメカニズムを理解すると、より複雑なプロジェクトで発生する可能性のあるパフォーマンスの問題を回避できます。 Python の組み込みツールを使用して、プログラムのメモリ管理動作を監視することもできます。

Python によるメモリの管理方法

すべての Python オブジェクトには、参照カウントとも呼ばれる参照カウントがあります。 refcount は、特定のオブジェクトへの参照を保持する他のオブジェクトの合計数です。オブジェクトへの参照を追加または削除すると、数値が増減します。オブジェクトの参照カウントがゼロになると、オブジェクトの割り当てが解除され、そのメモリが解放されます。

リファレンスとは何ですか?名前による、または別のオブジェクトのアクセサーを介したオブジェクトのコンテンツへのアクセスを許可します。

これは簡単な例です:

x = "Hello there"

このコマンドを Python に発行すると、内部で 2 つのことが起こります:

  1. 文字列「Hello there」が作成されます。 Python オブジェクトとしてメモリに保存されます。
  2. 名前 x がローカル名前空間に作成され、オブジェクトを指します。これにより、参照カウントが 1 から 1 に増加します。

y = x とすると、参照カウントは再び 2 に増加します。

xandy がスコープ外になるか名前空間から削除されるたびに、文字列の参照カウントは名前ごとに 1 ずつ減ります。 x と y の両方が範囲外になるか、削除されると、文字列の参照カウントは 0 になり、削除されます。

ここで、次のように文字列を含むリストを作成するとします。

x = ["Hello there", 2, False]

リスト自体が削除されるか、文字列を含む要素がリストから削除されるまで、文字列はメモリ内に残ります。 。これらの操作のいずれも、文字列への参照を保持している唯一のものを消去します。

次の例を考えてみましょう:

x = "Hello there" y = [x]

から最初の要素 y を削除するか、リスト y を完全に削除しても、文字列はまだメモリ内にあります。これは、名前 x にそれへの参照が含まれているためです。

Python の参照サイクル

ほとんどの場合、参照カウントは正常に機能します。ただし、2 つのオブジェクトがそれぞれ他方への参照を保持している状況に遭遇することがあります。これを基準期間といいます。この場合、オブジェクトの参照カウントがゼロになることはなく、メモリから削除されることもありません。

これは不自然な例です:

x = SomeClass()
y = SomeOtherClass()
x.item = y
y.item = x

x と y は相互参照を保持しているため、他のものがどちらかを参照していなくても、システムから削除されることはありません。

Python 自体のランタイムがオブジェクトの参照サイクルを生成することは、実際には非常に一般的です。例としては、例外自体への参照を含むトレースバック オブジェクトを含む例外があります。

以前のバージョンの Python では、これが問題でした。参照サイクルを持つオブジェクトは時間の経過とともに蓄積される可能性があり、これは長時間実行されるアプリケーションにとって大きな問題になります。しかし、Python はそれ以来、参照サイクルを管理するためにサイクル検出およびガベージ コレクション システムを導入しました。

Python ガベージ コレクター (gc)

Python のガベージ コレクターは、参照サイクルを持つオブジェクトを検出します。これは、「コンテナ」であるオブジェクト (リスト、辞書、カスタム クラス インスタンスなど) を追跡し、どのオブジェクトが他の場所からもアクセスできないかを判断することによって行われます。

これらのオブジェクトが選択されると、ガベージ コレクターは参照カウントが安全にゼロになることを確認してそれらを削除します。

Python オブジェクトの大部分には参照サイクルがないため、ガベージ コレクターを 24 時間年中無休で実行する必要はありません。代わりに、ガベージ コレクターはいくつかのヒューリスティックを使用して、実行頻度を減らし、毎回できるだけ効率的に実行します。

Python インタープリターは、開始されると、割り当てられているが解放されていないオブジェクトの数を追跡します。 Python オブジェクトの大部分は存続期間が短いため、すぐに表示されて消えます。しかし、時間が経つにつれて、より長寿命のオブジェクトが出現するでしょう。このようなオブジェクトが一定数以上蓄積されると、ガベージ コレクターが実行されます。

ガベージ コレクターが実行されるたびに、コレクションで生き残ったすべてのオブジェクトが収集され、それらが世代と呼ばれるグループに配置されます。これらの「第 1 世代」オブジェクトは、参照サイクル中にスキャンされる頻度が低くなります。ガベージ コレクターで生き残った第 1 世代のオブジェクトは、最終的には第 2 世代に移行され、スキャンの頻度が低くなります。

同样,垃圾收集器不会跟踪所有内容。例如,像用户创建的类这样的复杂对象总是被跟踪。但是不会跟踪仅包含简单对象(如整数和字符串)的字典,因为该特定字典中的任何对象都不会包含对其他对象的引用。不能保存对其他元素(如整数和字符串)的引用的简单对象永远不会被跟踪。

如何使用 gc 模块

通常,垃圾收集器不需要调整即可运行良好。Python 的开发团队选择了反映最常见现实世界场景的默认值。但是如果你确实需要调整垃圾收集的工作方式,你可以使用Python 的 gc 模块。该gc模块为垃圾收集器的行为提供编程接口,并提供对正在跟踪的对象的可见性。

gc当你确定不需要垃圾收集器时,你可以做的一件有用的事情是关闭它。例如,如果你有一个堆放大量对象的短运行脚本,则不需要垃圾收集器。脚本结束时,所有内容都将被清除。为此,你可以使用命令禁用垃圾收集器gc.disable()。稍后,你可以使用 重新启用它gc.enable()。

你还可以使用 手动运行收集周期gc.collect()。一个常见的应用是管理程序的性能密集型部分,该部分会生成许多临时对象。你可以在程序的该部分禁用垃圾收集,然后在最后手动运行收集并重新启用收集。

另一个有用的垃圾收集优化是gc.freeze(). 发出此命令时,垃圾收集器当前跟踪的所有内容都被“冻结”,或者被列为免于将来的收集扫描。这样,未来的扫描可以跳过这些对象。如果你有一个程序在启动之前导入库并设置大量内部状态,那么你可以gc.freeze()在所有工作完成后发出。这使垃圾收集器不必搜寻那些无论如何都不太可能被删除的东西。(如果你想对冻结的对象再次执行垃圾收集,请使用gc.unfreeze().)

使用 gc 调试垃圾收集

你还可以使用它gc来调试垃圾收集行为。如果你有过多的对象堆积在内存中并且没有被垃圾收集,你可以使用gc's 检查工具来找出可能持有对这些对象的引用的对象。

如果你想知道哪些对象持有对给定对象的引用,可以使用gc.get_referrers(obj)列出它们。你还可以使用gc.get_referents(obj)来查找给定对象引用的任何对象。

如果你不确定给定对象是否是垃圾收集的候选对象,gc.is_tracked(obj)请告诉你垃圾收集器是否跟踪该对象。如前所述,请记住垃圾收集器不会跟踪“原子”对象(例如整数)或仅包含原子对象的元素。

如果你想亲自查看正在收集哪些对象,可以使用 设置垃圾收集器的调试标志gc.set_debug(gc.DEBUG_LEAK|gc.DEBUG_STATS)。这会将有关垃圾收集的信息写入stderr。它将所有作为垃圾收集的对象保留在只读列表中。

避免 Python 内存管理中的陷阱

如前所述,如果你在某处仍有对它们的引用,则对象可能会堆积在内存中而不会被收集。这并不是 Python 垃圾收集本身的失败。垃圾收集器无法判断你是否不小心保留了对某物的引用。

让我们以一些防止对象永远不会被收集的指针作为结尾。

注意对象范围

如果你将对象 1 指定为对象 2 的属性(例如类),则对象 2 将需要超出范围,然后对象 1 才会:

obj1 = MyClass()
obj2.prop = obj1

更重要的是,如果这种情况发生在某种其他操作的副作用中,例如将对象 2 作为参数传递给对象 1 的构造函数,你可能不会意识到对象 1 持有一个引用:

obj1 = MyClass(obj2)

另一个例子:如果你将一个对象推入模块级列表并忘记该列表,则该对象将一直保留,直到从列表中删除,或者直到列表本身不再有任何引用。但是如果该列表是一个模块级对象,它可能会一直存在,直到程序终止。

简而言之,请注意你的对象可能被另一个看起来并不总是很明显的对象持有的方式。

使用 weakref避免引用循环

Python 的 weakref 模块允许你创建对其他对象的弱引用。弱引用不会增加对象的引用计数,因此只有弱引用的对象是垃圾回收的候选对象。

一个常见的用途weakref是对象缓存。你不希望仅仅因为它具有缓存条目而保留引用的对象,因此你将 aweakref用于缓存条目。

参照サイクルの手動解除

最後に、特定のオブジェクトに別のオブジェクトへの参照が含まれていることがわかっている場合は、いつでもそのオブジェクトへの参照を手動で解除できます。たとえば、instance_of_class.ref = other_object がある場合、instance_of_class を削除する準備ができたら、instance_of_class.ref = None を設定できます。

Python のメモリ管理の仕組みを理解することで、そのガベージ コレクション システムが Python プログラムのメモリの最適化にどのように役立つか、また、標準ライブラリなどで提供されるモジュールを使用してメモリ使用量とガベージ コレクションを制御する方法を見ていきます。 。

元のタイトル: Python ガベージ コレクションと gc モジュール

以上がPython のメモリ管理の仕組みを理解していますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は51cto.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。