ตัวเก็บขยะและการอ้างอิงแบบวนรอบ
เรียนรู้ว่าโมดูล gc จัดการวงจรการอ้างอิงอย่างไร
ตัวเก็บขยะและการอ้างอิงแบบวนรอบ เป็นบทเรียน 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 bgc.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 collectgc.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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การนับจำนวนการอ้างอิงของ CPython
- ตัวเก็บขยะและการอ้างอิงแบบวนรอบ
- การวิเคราะห์ประสิทธิภาพด้วย cProfile และ line_profiler
- การวิเคราะห์การใช้หน่วยความจำด้วย tracemalloc