>  기사  >  백엔드 개발  >  Python 메모리 관리가 어떻게 작동하는지 이해하고 있나요?

Python 메모리 관리가 어떻게 작동하는지 이해하고 있나요?

WBOY
WBOY앞으로
2023-04-12 16:25:091197검색

Python은 개발자에게 많은 편의를 제공하며, 가장 큰 장점 중 하나는 사실상 걱정 없는 메모리 관리입니다. 개발자는 더 이상 Python의 개체 및 데이터 구조에 대한 메모리를 수동으로 할당, 추적 및 해제할 필요가 없습니다. 런타임이 이 모든 작업을 수행하므로 시스템 수준의 세부 사항을 다루기보다는 실제 문제를 해결하는 데 집중할 수 있습니다.

Python 메모리 관리가 어떻게 작동하는지 이해하고 있나요?

경험이 부족한 Python 사용자라도 Python의 가비지 수집 및 메모리 관리 작동 방식을 이해하는 것이 좋습니다. 이러한 메커니즘을 이해하면 보다 복잡한 프로젝트에서 발생할 수 있는 성능 문제를 방지하는 데 도움이 됩니다. Python에 내장된 도구를 사용하여 프로그램의 메모리 관리 동작을 모니터링할 수도 있습니다.

Python이 메모리를 관리하는 방법

모든 Python 객체에는 참조 카운트라고도 하는 참조 카운트가 있습니다. refcount는 주어진 객체에 대한 참조를 보유하는 다른 객체의 총 개수입니다. 개체에 대한 참조를 추가하거나 제거하면 숫자가 올라가거나 내려갑니다. 개체의 참조 횟수가 0에 도달하면 개체 할당이 취소되고 해당 메모리가 해제됩니다.

레퍼런스란 무엇인가요? 이름으로 또는 다른 개체의 접근자를 통해 개체의 모든 콘텐츠에 액세스할 수 있습니다.

다음은 간단한 예입니다.

x = "Hello there"

이 명령을 Python에 실행하면 내부적으로 두 가지 일이 발생합니다.

  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의 참조 순환

대부분의 경우 참조 카운팅은 잘 작동합니다. 그러나 때로는 두 개체가 서로에 대한 참조를 보유하는 상황에 직면할 때도 있습니다. 이를 참조 기간이라고 합니다. 이 경우 객체의 참조 횟수는 결코 0에 도달하지 않으며 메모리에서 삭제되지도 않습니다.

이것은 인위적인 예입니다:

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

x와 y는 서로에 대한 참조를 보유하므로 둘 중 하나를 참조하는 다른 항목이 없더라도 시스템에서 삭제되지 않습니다.

실제로 Python 자체 런타임에서 객체에 대한 참조 순환을 생성하는 것은 매우 일반적입니다. 예를 들어 예외 자체에 대한 참조가 포함된 추적 객체의 예외가 있습니다.

이전 버전의 Python에서는 이것이 문제였습니다. 참조 순환이 있는 객체는 시간이 지남에 따라 누적될 수 있으며 이는 장기 실행 애플리케이션에 큰 문제입니다. 그러나 Python은 이후 참조 순환을 관리하기 위해 순환 감지 및 가비지 수집 시스템을 도입했습니다.

Python 가비지 수집기(gc)

Python의 가비지 수집기는 참조 순환을 사용하여 객체를 감지합니다. 이는 "컨테이너"인 개체(예: 목록, 사전, 사용자 정의 클래스 인스턴스)를 추적하고 그 중 다른 곳에서는 액세스할 수 없는 개체를 결정함으로써 이를 수행합니다.

이러한 객체가 선택되면 가비지 수집기는 참조 횟수가 안전하게 0으로 떨어질 수 있도록 보장하여 객체를 제거합니다.

대부분의 Python 객체에는 참조 주기가 없으므로 가비지 수집기가 연중무휴로 실행될 필요가 없습니다. 대신 가비지 수집기는 몇 가지 경험적 방법을 사용하여 실행 빈도를 줄이고 매번 최대한 효율적으로 실행합니다.

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用于缓存条目。

수동으로 참조 순환 끊기

마지막으로, 주어진 개체에 다른 개체에 대한 참조가 포함되어 있다는 것을 알고 있는 경우 언제든지 해당 개체에 대한 참조를 수동으로 끊을 수 있습니다. 예를 들어, 인스턴스_of_class.ref = other_object가 있는 경우, 인스턴스_of_class를 삭제할 준비가 되면 인스턴스_of_class.ref = None을 설정할 수 있습니다.

Python 메모리 관리 작동 방식을 이해함으로써 가비지 수집 시스템이 Python 프로그램에서 메모리 최적화에 어떻게 도움이 되는지, 그리고 표준 라이브러리 및 다른 곳에서 제공되는 모듈을 사용하여 메모리 사용 및 가비지 수집을 제어할 수 있는 방법을 살펴봅니다.

원제:​​Python 가비지 수집 및 gc 모듈​

위 내용은 Python 메모리 관리가 어떻게 작동하는지 이해하고 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 51cto.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제