1

CPython에는 서로 밀접하게 연결된 두 가지 유형의 객체가 있습니다. 각각을 만들고, 다른에 대한 참조를 유지하기 때문에 내 테스트 스크립트를 사용CPython에서 직접 참조주기를 끊는 방법

#include <Python.h> 
#include <structmember.h> 

typedef struct pyt PYT; 
struct pyt { PyObject_HEAD PYT *other; }; 

static void dealloc (PYT *self) { 
    Py_CLEAR(self->other); 
    printf("dealloc object at %p\n", self); 
    PyObject_GC_Del(self); 
} 

static PyTypeObject Pyt2Type = { 
    PyObject_HEAD_INIT(NULL) 
    0, "pyt.Pyt2", sizeof(PYT), 0, 
    (destructor) dealloc 
}; 

static PyObject * new (PyTypeObject *type, PyObject *args, PyObject *kwds) { 
    PYT *self = PyObject_GC_New(PYT, type); 
    if (!self) return NULL; 
    self->other = PyObject_GC_New(PYT, &Pyt2Type); 
    if (!self->other) { Py_DECREF(self); return NULL; } 
    return Py_INCREF(self), self->other->other = self, (PyObject *) self; 
} 

static PyTypeObject Pyt1Type = { 
    PyObject_HEAD_INIT(NULL) 
    0, "pyt.Pyt1", sizeof(PYT), 0, 
    (destructor) dealloc 
}; 

static int traverse (PYT *self, visitproc visit, void *arg) { 
    Py_VISIT(self->other); 
    return 0; 
} 

static int clear (PYT *self) { 
    Py_CLEAR(self->other); 
    return 0; 
} 

static PyMemberDef members[] = { 
    {"other", T_OBJECT, offsetof(PYT, other), RO, "other"}, 
    { NULL } 
}; 

static PyMethodDef methods[] = {{ NULL }}; 

PyMODINIT_FUNC initpyt (void) { 
    PyObject* m; 

    Pyt1Type.tp_flags = Pyt2Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; 
    Pyt1Type.tp_traverse = Pyt2Type.tp_traverse = (traverseproc) traverse; 
    Pyt1Type.tp_clear = Pyt2Type.tp_clear = (inquiry) clear; 
    Pyt1Type.tp_members = Pyt2Type.tp_members = members; 
    Pyt1Type.tp_new = new; 

    if (PyType_Ready(&Pyt1Type) < 0) return; 
    if (PyType_Ready(&Pyt2Type) < 0) return; 

    m = Py_InitModule("pyt", methods); 

    Py_INCREF(&Pyt1Type), PyModule_AddObject(m, "Pyt", (PyObject *) &Pyt1Type); 
} 

from distutils.core import Extension, setup 
import sys, gc 
sys.argv.extend(["build_ext", "-i"]) 
setup(ext_modules = [Extension('pyt', ['pyt.c'])]) 
from pyt import Pyt 
pyt = Pyt() 
print pyt, sys.getrefcount(pyt) 
pyt = pyt.other 
print pyt, sys.getrefcount(pyt) 
del pyt 
gc.collect() 

나는

<pyt.Pyt1 object at 0x7fbc26540138> 3 
<pyt.Pyt2 object at 0x7fbc26540150> 3 

객체가 마지막에 삭제되지 않습니다 같은 출력을 얻을 닫힌 사이클. 다른 코드에서는 두 가지 모두가 0의 참조를 가질 때까지 객체를 보관하는 방식을 사용했습니다. 이는 나쁜 습관으로 의심됩니다. 이제는 여기에서 가비지 컬렉터를 사용해 보았지만 여전히 오브젝트는 수집되지 않았습니다.

여기에 무슨 문제가 있습니까? 내가 놓친 게 무엇입니까?

+0

의 출력으로

static PyObject * new (PyTypeObject *type, PyObject *args, PyObject *kwds) { PYT *self = PyObject_GC_New(PYT, type); if (!self) return NULL; self->other = PyObject_GC_New(PYT, &Pyt2Type); if (!self->other) { Py_DECREF(self); return NULL; } self->other->other = (Py_INCREF(self), self); PyObject_GC_Track((PyObject *) self); PyObject_GC_Track((PyObject *) self->other); return (PyObject *) self; } 

; 여전히 컬렉션이 없습니다. – tynn

답변

0

좋아, 나는 마침내 내 문제를 발견했다. PyObject_GC_Track으로 추적을 시작하지 않았습니다.

파이썬 일부 단계가 필요 가비지 수집기 사용하는 경우 : 추가

  • Py_TPFLAGS_HAVE_GC
  • tp_traverse을 첨가 PyObject_GC_New 또는 유사한 기능, 필요한 경우 tp_clear 기능
  • 객체 생성 tp_flags
  • 완전히 초기화 된 개체에 PyObject_GC_Track을 호출하십시오.
  • PyObject_GC_Del 또는 유사한 기능으로 개체 삭제

여기에서 new 함수를 수정하면 충분합니다.I 가비지 수집기 인터페이스를 구현하고, 질문을 업데이트

<pyt.Pyt1 object at 0x7f4904fe1398> 4 
<pyt.Pyt2 object at 0x7f4904fe15c8> 4 
dealloc object at 0x7f4904fe15c8 
dealloc object at 0x7f4904fe1398 
0

약한 참조를 사용하여이를 수행 할 수 있습니다 (weakref 모듈 참조). 그러나 일반적으로 가비지 수집기에만 의존하는 것이 좋습니다. 다른 사람들이 여러분의 객체를 포함하는 큰 레퍼런스 사이클을 생성 할 수 있으며, 그러면 어쨌든 GC에 의존 할 것이므로 간단한 경우에 사용할 수 있습니다.

"나쁘게 실패했습니다."라는 것이 무슨 뜻인지 설명해주십시오.

+0

두 개체가 함께 생존해야하므로이 강력한 참조가 필요합니다. GC를 잘못 사용하여 실패한 것 같습니다. 트래버스하고 올바르게 지우는 개념을 이해하지 못했거나 내가 보지 못했던 실수를했기 때문입니다. 나는 이것을 다시 시도 할 것이다. – tynn

0

(대부분의) 가비지 수집 언어에 대해주의해야 할 중요한 점은 객체 삭제가 객체에 도달 할 수있게되는 즉시 이루어지지 않는다는 것입니다. 일단 객체가 도달 할 수 없게되면 연관된 자원을 해제 할 때까지 가비지 수집기에 달려 있습니다. 메모리에 대한 부담이 없으면 프로그램이 종료 될 때까지 늦을 수 있습니다.

연결된 클래스에 __del__ 메서드를 설정하지 않으면 가비지 수집기가 정상적으로 작동합니다. 참조 사이클을 감지하는 기능이 단순한 참조 카운팅보다 비용이 많이 들고 즉 드물게 실행되므로 개체를 즉시 정리하지 않습니다. 순수 파이썬 클래스

import gc 
import weakref 

class Obj(object): pass 

x = Obj() 
y = Obj() 

x.y = y, y.x = x 

ref = weakref.ref(x) 

print(ref()) 
del x, y 
print(ref()) 
gc.collect() 
print(ref()) 

출력을 사용하여

예 :

<__main__.Obj object at 0x7f81c8ccc7b8> 
<__main__.Obj object at 0x7f81c8ccc7b8> 
None 
+0

CPython은 특별합니다. 모든 객체는 참조 카운트이며 GC는 이론적으로 선택 사항입니다 (그러나 AFAIK는 올바른 마음에 아무도 실제로 그것을 끕니다). – Kevin