0Pricing
Python Academy · บทเรียน

การนับจำนวนการอ้างอิงของ CPython

ทำความเข้าใจว่า CPython ติดตามอายุการใช้งานของออบเจ็กต์ด้วยตัวนับการอ้างอิงอย่างไร

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

การนับจำนวนการอ้างอิงคืออะไร

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

import sys

x = [1, 2, 3]
print(sys.getrefcount(x))   # 2 (x + getrefcount arg)
y = x
print(sys.getrefcount(x))   # 3

sys.getrefcount()

sys.getrefcount(obj) ส่งคืนจำนวนการอ้างอิงปัจจุบัน โปรดทราบว่าเมื่อเรียกใช้ฟังก์ชันนี้ จำนวนจะเพิ่มขึ้น 1 (จากการอ้างอิงของอาร์กิวเมนต์ฟังก์ชัน)

import sys

a = "hello"
print(sys.getrefcount(a))   # baseline (may be high for interned strings)

b = a
print(sys.getrefcount(a))   # one more
del b
print(sys.getrefcount(a))   # back to baseline

การเพิ่มและการลดจำนวน

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

x = []          # refcount = 1
y = x           # refcount = 2
z = [x]         # refcount = 3  (list holds a reference)
del y           # refcount = 2
z.clear()       # refcount = 1
del x           # refcount = 0 → deallocated

การใช้อ็อบเจ็กต์ร่วมกัน

CPython เก็บจำนวนเต็มขนาดเล็ก (-5 ถึง 256) และลิเทอรัลสตริงจำนวนมากไว้ใช้ร่วมกัน โดยนำอ็อบเจ็กต์เดียวกันกลับมาใช้ การเปรียบเทียบด้วย is จะแสดงให้เห็นพฤติกรรมนี้

a = 256
b = 256
print(a is b)   # True (interned)

c = 257
d = 257
print(c is d)   # may be False (not interned)

การอ้างอิงแบบอ่อน

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

import weakref

class BigObject:
    pass

obj = BigObject()
ref = weakref.ref(obj)
print(ref())   # <BigObject instance>
del obj
print(ref())   # None (object was collected)

วัฏจักรการอ้างอิง

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

a = []
b = [a]
a.append(b)   # cycle: a → b → a
del a, b
# Both are unreachable but refcount > 0
# The cyclic GC collects them

โมดูล gc

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

import gc

gc.collect()   # force a collection cycle
print(gc.get_count())    # (gen0, gen1, gen2) allocation counts
print(gc.get_threshold())  # when each generation is collected

พูลหน่วยความจำ (pymalloc)

CPython จัดสรรอ็อบเจ็กต์ขนาดเล็ก (< 512 ไบต์) จากพูลหน่วยความจำของตนเอง (pymalloc) ซึ่งเร็วกว่าการเรียกใช้ malloc สำหรับอ็อบเจ็กต์ขนาดเล็กทุกตัว

# You do not call pymalloc directly — CPython uses it internally
# for all Python-level object allocations

# The pool pre-allocates large memory arenas
# and carves them into pools of fixed-size blocks

ตัวเก็บทรัพยากร __del__

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

class Resource:
    def __del__(self):
        print(f"Freeing {self}")

# Better: use context manager
class SafeResource:
    def __enter__(self): return self
    def __exit__(self, *a): self.cleanup()

อ็อบเจ็กต์ขนาดกะทัดรัดด้วย __slots__

อ็อบเจ็กต์ที่มี __slots__ ใช้หน่วยความจำและการจัดสรรน้อยลง จึงลดภาระของ GC

import sys

class Reg:
    def __init__(self, x, y): self.x, self.y = x, y

class Slotted:
    __slots__ = ("x","y")
    def __init__(self, x, y): self.x, self.y = x, y

print(sys.getsizeof(Reg(1,2)))      # ~48 + dict overhead
print(sys.getsizeof(Slotted(1,2)))  # ~48 (no dict)

การปิดใช้งาน GC วัฏจักร

สำหรับโปรแกรมที่ไม่สร้างวัฏจักรการอ้างอิง การปิดใช้งาน GC วัฏจักรอาจช่วยเพิ่มปริมาณงานได้

import gc

gc.disable()   # no cyclic GC — objects are only freed by refcounting

# Use only if you KNOW no cycles are created
# (pure functional/immutable data)

gc.enable()    # re-enable

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

CPython จะคืนพื้นที่ของอ็อบเจ็กต์ทันทีเมื่อใด

สรุป

CPython ติดตามอ็อบเจ็กต์ผ่านจำนวนการอ้างอิง จำนวนเหลือ 0 → คืนพื้นที่ทันที วัฏจักรการอ้างอิงได้รับการจัดการโดย GC วัฏจักร (โมดูล gc) หลีกเลี่ยง __del__ สำหรับการคืนทรัพยากรที่สำคัญ ให้ใช้ตัวจัดการบริบทแทน ใช้ __slots__ เพื่อลดหน่วยความจำที่ใช้ต่ออ็อบเจ็กต์

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

บทเรียน “การนับจำนวนการอ้างอิงของ CPython” ฟรีหรือไม่

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

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

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

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

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

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

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

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

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

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

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