2012-10-25 5 views
5

Python C API를 사용할 때 참조 카운트를 처리하는 방법을 이해하려고합니다.Python C API, 새로운 객체에 대한 참조 횟수가 높습니다.

나는 다음과 같이 C++에서 파이썬 함수를 호출 할 :

PyObject* script; 
PyObject* scriptRun; 
PyObject* scriptResult; 

// import module 
script = PyImport_ImportModule("pythonScript"); 
// get function objects 
scriptRun = PyObject_GetAttrString(script, "run"); 
// call function without/empty arguments 
scriptResult = PyObject_CallFunctionObjArgs(scriptRun, NULL); 

if (scriptResult == NULL) 
    cout << "scriptResult = null" << endl; 
else 
    cout << "scriptResult != null" << endl; 

cout << "print reference count: " << scriptResult->ob_refcnt << endl; 
pythonScript.py에서

파이썬 코드는 매우 간단합니다 :

def run(): 
    return 1 

"PyObject_CallFunctionObjArgs"의 설명서를 말한다 당신은 반환 값으로 새로운 참조를 얻습니다. 내가 참조 카운트를 감소시키지 않고 루프에서이 작업을 수행 할 경우

scriptResult != null 
print reference count: 72 

이 또한 내가 메모리 누수가 기대 : 출력은 그러나 그래서 1의 참조 카운트를 갖고 "scriptResult"을 기대. 그러나 이것은 일어나지 않는 것 같습니다.

누군가 나를 이해하도록 도와 줄 수 있습니까?

친절에 감사드립니다!

+0

후속 질문 : @ecatmur 및 ​​@KayZhu 덕분에 이제는 메모리 누수가없는 이유를 알았습니다. 그럼에도 불구하고이 코드를 긴 루프에서 실행하면 완전한 OS가 어쨌든 으스러지게됩니다. '1'에 대한 레퍼런스 카운트가 매 반복마다 증가하고 있지만 이것이 왜 시스템 실패를 일으키는 지 알지 못합니다. – user1774143

+0

'ob_refcnt'가 0 번 순환 할 때까지 루핑을하고 있습니까? 참조 횟수가 1로 많이 변동합니다. 지난 0을 감싸는 경우, 정상적인 연산은'Py_DECREF'를 0으로 할 수 있고'int' 1은 할당 해제되고, segfault에 의해 빠르게 따라옵니다. 덜 일반적인 intern 같은 1312와 같은 int를 시도해보십시오. – eryksun

+0

적어도 sys.maxint (내 시스템에서는 '9223372036854775807')까지 반복하지 않았습니다. 오늘 나는 오류를 재현 할 수없는 것 같아 내 작업 바탕 화면을 쏘려고하지 말아야한다고 생각합니다. 당신의 도움을 주셔서 감사합니다! – user1774143

답변

2

ecatmur가 맞습니다. 숫자와 문자열은 파이썬에서 사용되지 않으므로 간단한 object() 개체로 시도해 볼 수 있습니다.

gc 간단한 데모 :

import gc 


def run(): 
    return 1 

s = run() 
print len(gc.get_referrers(s)) # prints a rather big number, 41 in my case 

obj = object() 
print len(gc.get_referrers(obj)) # prints 1 

lst = [obj] 
print len(gc.get_referrers(obj)) # prints 2 

lst = [] 
print len(gc.get_referrers(obj)) # prints 1 again 

좀더 : CPython의 새로운 객체를 생성 할 때, 그것은 호출 C 1.이어서 Py_INCREF(op)Py_DECREF(op) 사용하는 레퍼런스 카운트를 초기화 _Py_NewReference를 매크로 참조 횟수를 늘리거나 줄입니다.

+0

고마워요! 그러나 내가 123456.7565와 같은 숫자로 s를 대체하면 나는 여전히 1 개의 리퍼러를 얻는다. 내 파이썬 코드에서 내 C++ 코드로 호출하면 여전히 2가됩니다. 이것이 어떤 이유 일 수 있습니까? – user1774143

+1

@ user1774143 :'run()'함수에서'123456.7565'를 돌려 주나요? 그렇다면 두 번째 참조는 코드 객체의 상수 튜플 인'run .__ code __. co_consts'입니다. – eryksun

+0

아, 이해합니다. 이 튜플에서 메서드 내의 모든 상수가 저장되므로 여기에 추가 참조가 하나 있습니다. 감사! – user1774143

4

혼란 (또한 True, False, None 단일 문자열 등)를 작은 정수들은 프로그램에서 사용되거나 얻어 목적지 런타임 시도한다는 것을 의미하는, 는 구금 ("is" operator behaves unexpectedly with integers)이라는 것이다

>>> 1 is 1 
True 
>>> 1 + 1 is 2 
True 
>>> 1000 + 1 is 1001 
False 

이것은 당신이 return 1 쓸 때 (당신이 보았 듯이), 당신이 상당한 참조 카운트 이미 존재하는 int 개체 인스턴스를 반환하고 있음을 의미합니다 : 같은 개체 인스턴스를 사용할 수 있습니다. 같은 인스턴스가 다른 곳에서 사용되기 때문에 참조를 해제하지 않아도 메모리 누수가 발생하지는 않습니다.

return 1001 또는 return object()으로 스크립트를 변경하면 초기 참조 카운트 1과 메모리 누수가 표시됩니다.

+0

빠른 답장을 보내 주셔서 감사합니다, 나는 작은 정수에 대해 몰랐습니다! 그러나 반환 값을 1001 또는 다른 재미있는 값으로 변경하면 여전히 1 대신 2의 참조 횟수가 발생합니다. – user1774143

+0

괜찮습니다. 이제는 object()를 반환 할 때 참조 횟수가 실제로 1임을 알 수 있습니다! – user1774143

관련 문제