Python Academy · บทเรียน

ตัวเก็บขยะและการอ้างอิงแบบวนรอบ

เรียนรู้ว่าโมดูล gc จัดการวงจรการอ้างอิงอย่างไร

บทเรียน 2 จาก 413 ขั้นตอน

ตัวเก็บขยะและการอ้างอิงแบบวนรอบ เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดการนับจำนวนการอ้างอิงจึงไม่เพียงพอ

การนับจำนวนการอ้างอิงไม่สามารถเก็บอ็อบเจ็กต์ที่อยู่ในวัฏจักรการอ้างอิงได้ เพราะแต่ละอ็อบเจ็กต์ถือการอ้างอิงไปยังอีกอ็อบเจ็กต์หนึ่ง ดังนั้นจำนวนของทั้งคู่จึงไม่ลดเหลือ 0

# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect them

โมดูล gc

gc คือตัวเก็บขยะวัฏจักร ซึ่งจะสแกนอ็อบเจ็กต์ที่ติดตามเป็นระยะเพื่อค้นหาวัฏจักรที่ไม่สามารถเข้าถึงได้ แล้วเก็บอ็อบเจ็กต์เหล่านั้น

import gc

gc.collect()   # trigger immediately
print(gc.garbage)   # objects with __del__ in cycles (cannot auto-collect)

การเก็บขยะแบบแบ่งรุ่น

GC ใช้สามรุ่น อ็อบเจ็กต์ใหม่เริ่มต้นที่รุ่น 0 อ็อบเจ็กต์ที่รอดจากการเก็บขยะจะถูกเลื่อนขึ้นไปยังรุ่นถัดไป รุ่น 0 จะถูกเก็บขยะบ่อยที่สุด

import gc

print(gc.get_count())     # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10)  # tune

การตรวจจับวัฏจักรด้วย gc.get_referents

ตรวจสอบว่าอ็อบเจ็กต์หนึ่งอ้างอิงถึงอ็อบเจ็กต์ใดบ้าง ซึ่งมีประโยชน์ต่อการทำความเข้าใจว่าเหตุใดบางสิ่งจึงไม่ถูกเก็บขยะ

import gc

a = []
b = [a]
a.append(b)

for ref in gc.get_referents(a):
    print(ref)   # shows b

gc.get_objects()

gc.get_objects() ส่งคืนอ็อบเจ็กต์ทั้งหมดที่ GC กำลังติดตามอยู่ เหมาะสำหรับค้นหาหน่วยความจำรั่ว

import gc

before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")

การหลีกเลี่ยงวัฏจักร

ออกแบบโครงสร้างข้อมูลเพื่อหลีกเลี่ยงวัฏจักร: ใช้ weakrefs สำหรับการอ้างอิงย้อนกลับ ใช้ ID แทนการอ้างอิงโดยตรง หรือทำลายวัฏจักรอย่างชัดเจนก่อนที่อ็อบเจ็กต์จะพ้นขอบเขต

import weakref

class Node:
    def __init__(self, parent):
        # weakref avoids cycle:
        self.parent = weakref.ref(parent)

class Tree:
    def __init__(self):
        self.child = Node(self)   # no hard cycle

__del__ และวัฏจักร

อ็อบเจ็กต์ที่มี __del__ และอยู่ในวัฏจักรจะไม่สามารถถูกเก็บโดยอัตโนมัติได้ แต่อ็อบเจ็กต์เหล่านั้นจะถูกนำไปไว้ใน gc.garbage แทน

import gc

class Leaky:
    def __del__(self): pass

a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b

gc.collect()
print(gc.garbage)   # [Leaky, Leaky] — cannot collect

gc.freeze() — Python 3.7 ขึ้นไป

gc.freeze() จะตรึงอ็อบเจ็กต์ที่กำลังถูกติดตามอยู่ในปัจจุบัน เพื่อไม่ให้ถูกเก็บขยะอีก เหมาะสำหรับกระบวนการที่ทำงานเป็นเวลานานหลังเริ่มต้นระบบ เพื่อช่วยลดการหยุดชะงักจาก GC

import gc

# After all globals and imports are in place:
gc.freeze()   # no GC scanning for these objects ever again

# Useful for: gunicorn pre-fork, background servers

การปิดและเปิดใช้ GC อีกครั้ง

ปิดใช้ GC วัฏจักรในลูปที่ทำงานต่อเนื่องและแน่นหนาเมื่อทราบว่าไม่มีวัฏจักร จากนั้นเปิดใช้อีกครั้งและบังคับให้เก็บขยะหลังจากนั้น

import gc

gc.disable()
try:
    result = tight_loop_no_cycles()
finally:
    gc.enable()
    gc.collect()

การตรวจสอบ isenabled และ isfinalized

ตรวจสอบสถานะของ GC ด้วย gc.isenabled() และตรวจสอบว่าอ็อบเจ็กต์กำลังถูกเรียกคืนทรัพยากรหรือไม่ด้วย gc.is_finalized(obj)

import gc

print(gc.isenabled())      # True by default
obj = object()
print(gc.is_finalized(obj))  # False (still alive)

รูปแบบที่ทำให้เกิดหน่วยความจำรั่ว

สาเหตุทั่วไปของหน่วยความจำรั่วใน Python ได้แก่ แคชส่วนกลางที่เติบโตไม่จำกัด ตัวรับฟังเหตุการณ์ที่ไม่ได้ถอดออก การอ้างอิงแบบวัฏจักรที่มี __del__ และส่วนขยาย C ที่ทำให้การอ้างอิงรั่วไหล

# Typical leaks:
global_cache = {}   # grows forever — use lru_cache with maxsize

class EventBus:
    listeners = []
    # Listeners hold references to objects:
    # @classmethod
    # def on(cls, fn): cls.listeners.append(fn)
    # Must call off() to detach

ตรวจสอบอย่างรวดเร็ว

อ็อบเจ็กต์ที่มี __del__ และอยู่ในวัฏจักรการอ้างอิงจะเกิดอะไรขึ้น

สรุป

GC วัฏจักรจัดการวัฏจักรการอ้างอิงที่การนับจำนวนการอ้างอิงไม่สามารถจัดการได้ โดยใช้สามรุ่นและเลื่อนอ็อบเจ็กต์ที่รอดชีวิตขึ้นไปยังรุ่นถัดไป หลีกเลี่ยงวัฏจักรด้วย weakrefs อ็อบเจ็กต์ที่มี __del__ และอยู่ในวัฏจักรจะไปอยู่ใน gc.garbage ใช้ gc.freeze() หลังเริ่มต้นระบบเพื่อลดการหยุดชะงักจาก GC ในเซิร์ฟเวอร์ที่ทำงานเป็นเวลานาน

เริ่มต้นได้ฟรี

เรียนรู้ Python ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
76
บทเรียน
320

คำถามที่พบบ่อย

บทเรียน “ตัวเก็บขยะและการอ้างอิงแบบวนรอบ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวเก็บขยะและการอ้างอิงแบบวนรอบ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวเก็บขยะและการอ้างอิงแบบวนรอบ”

เรียนรู้ว่าโมดูล gc จัดการวงจรการอ้างอิงอย่างไร คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “ตัวเก็บขยะและการอ้างอิงแบบวนรอบ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม

ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การนับจำนวนการอ้างอิงของ CPython
  2. ตัวเก็บขยะและการอ้างอิงแบบวนรอบ
  3. การวิเคราะห์ประสิทธิภาพด้วย cProfile และ line_profiler
  4. การวิเคราะห์การใช้หน่วยความจำด้วย tracemalloc
← กลับไปที่ Python Academy