문제

스마트 포인터로 인한 메모리 누수를 발견하는 "기술"을 아는 사람이 있나요?나는 현재 다음 언어로 작성된 대규모 프로젝트를 진행 중입니다. C++ 참조 카운팅과 함께 스마트 포인터를 많이 사용합니다.분명히 스마트 포인터로 인해 메모리 누수가 발생하고 코드 어딘가에서 여전히 참조되므로 해당 메모리가 해제되지 않습니다.해당 객체가 더 이상 사용되지 않더라도 해당 객체가 해제되지 않도록 하는 "불필요한" 참조가 있는 코드 줄을 찾는 것은 매우 어렵습니다.

웹에서 참조 카운터의 증가/감소 작업에 대한 호출 스택을 수집하라는 조언을 찾았습니다.이는 어떤 코드 조각으로 인해 참조 카운터가 증가하거나 감소했는지에 대한 좋은 힌트를 제공합니다.

하지만 나에게 필요한 것은 해당 "증가/감소 호출 스택"을 함께 그룹화하는 일종의 알고리즘입니다.이러한 호출 스택 쌍을 제거한 후에는 (적어도) 하나의 "호출 스택 증가"가 남아 있기를 바랍니다. 이는 "불필요한" 참조가 포함된 코드 조각을 보여 주므로 해당 객체가 해제되지 않게 됩니다.이제 누출 문제를 해결하는 것은 큰 문제가 아닙니다!

그러나 그룹화를 수행하는 "알고리즘"에 대한 아이디어가 있는 사람이 있습니까?

개발은 아래에서 이루어집니다. 윈도우 XP.

(누군가가 내가 설명하려고 한 것을 이해했으면 좋겠습니다 ...)

편집하다:순환 참조로 인한 누수에 대해 이야기하고 있습니다.

도움이 되었습니까?

해결책

누출 원인 중 하나는 다음과 같습니다. 참조 카운팅 스마트 포인터 포인터는 다음과 같습니다 순환 종속성.예를 들어 A에는 B에 대한 스마트 포인터가 있고 B에는 A에 대한 스마트 포인터가 있습니다.A도 B도 파괴되지 않습니다.종속성을 찾아서 중단해야 합니다.

가능하다면 부스트 스마트 포인터를 사용하고, 데이터의 소유자로 추정되는 포인터에는 shared_ptr을 사용하고, 삭제를 호출하지 않는 포인터에는 약한_ptr을 사용하세요.

다른 팁

내가 하는 방법은 간단하다:- 모든 addRef () 레코드 콜 스택에서 - 일치하는 release ()가 제거됩니다.이렇게 하면 프로그램이 끝날 때 릴리스를 일치시키지 않고 AddRefs()만 남게 됩니다.쌍을 일치시킬 필요가 없습니다.

결정론적인 방식으로 누수를 재현할 수 있는 경우 제가 자주 사용한 간단한 기술은 생성 순서에 따라 모든 스마트 포인터에 번호를 매기고(생성자의 정적 카운터 사용) 이 ID를 누수와 함께 보고하는 것입니다.그런 다음 프로그램을 다시 실행하고 동일한 ID를 가진 스마트 포인터가 생성되면 DebugBreak()를 트리거합니다.

다음과 같은 훌륭한 도구도 고려해야 합니다. http://www.codeproject.com/KB/applications/visualleakDetector.aspx

참조 순환을 감지하려면 참조 횟수가 계산된 모든 개체의 그래프가 필요합니다.이러한 그래프는 구성하기가 쉽지 않지만 수행할 수 있습니다.

글로벌 생성 set<CRefCounted*> 살아있는 참조 카운트 객체를 등록합니다.공통 AddRef() 구현이 있으면 더 쉽습니다. this 객체의 참조 카운트가 0에서 1로 바뀔 때 집합에 대한 포인터입니다.마찬가지로 Release()에서는 참조 횟수가 1에서 0으로 변할 때 세트에서 객체를 제거합니다.

다음으로, 각 개체에서 참조된 개체 집합을 가져오는 몇 가지 방법을 제공합니다. CRefCounted*.그것은 virtual set<CRefCounted*> CRefCounted::get_children() 또는 당신에게 맞는 무엇이든.이제 그래프를 따라가는 방법이 생겼습니다.

마지막으로 선호하는 알고리즘을 구현해 보세요. 유향 그래프에서 사이클 감지.프로그램을 시작하고 일부 사이클을 생성한 다음 사이클 감지기를 실행합니다.즐기다!:)

내가 하는 일은 스마트 포인터를 다음 클래스로 래핑하는 것입니다. 기능 그리고 매개변수.생성자가 호출될 때마다 해당 함수와 줄의 개수가 증가하고 소멸자가 호출될 때마다 개수가 감소합니다.그런 다음 함수/라인/카운트 정보를 덤프하는 함수를 작성하세요.모든 참조가 생성된 위치를 알려줍니다.

이 문제를 해결하기 위해 내가 한 일은 malloc/새로 만들기 & 무료/삭제 수행 중인 작업에 대해 가능한 한 많이 데이터 구조를 추적하는 연산자입니다.

예를 들어 재정의할 때 malloc/새로 만들기, 호출자 주소, 요청된 바이트 양, 반환된 할당된 포인터 값 및 시퀀스 ID에 대한 레코드를 생성하여 모든 레코드를 순서대로 정렬할 수 있습니다. (스레드를 다루는지 여부는 모르겠지만 이를 가져가야 합니다. 계정도 마찬가지).

글을 쓸 때 무료/삭제 루틴에서는 호출자의 주소와 포인터 정보도 추적합니다.그런 다음 목록을 거꾸로 살펴보고 일치하는 항목을 찾으려고 합니다. malloc/새로 만들기 포인터를 내 키로 사용하는 상대방.찾지 못하면 위험 신호를 보내주세요.

여유가 있다면 데이터에 시퀀스 ID를 삽입하여 누가 언제 할당 호출을 했는지 확실하게 확인할 수 있습니다.여기서 핵심은 가능한 한 각 거래 쌍을 고유하게 식별하는 것입니다.

그런 다음 각 트랜잭션을 호출하는 함수와 함께 메모리 할당/할당 해제 내역을 표시하는 세 번째 루틴을 갖게 됩니다.(이것은 링커에서 기호 맵을 구문 분석하여 수행할 수 있습니다.)언제든지 할당할 메모리 양과 할당한 사람이 누구인지 알 수 있습니다.

이러한 트랜잭션을 수행할 리소스가 충분하지 않은 경우(8비트 마이크로 컨트롤러의 일반적인 경우) 직렬 또는 TCP 링크를 통해 리소스가 충분한 다른 시스템에 동일한 정보를 출력할 수 있습니다.

누수를 찾는 것이 문제가 아닙니다.스마트 포인터의 경우 수천 번 호출되는 CreateObject()와 같은 일반적인 위치로 연결될 가능성이 높습니다.코드에서 참조 계산된 개체에 대해 Release()를 호출하지 않은 위치를 결정하는 문제입니다.

Windows를 사용하고 있다고 말씀하셨기 때문에 Microsoft의 사용자 모드 덤프 힙 유틸리티를 활용하실 수도 있습니다. UMDH, 이는 Windows용 디버깅 도구.UMDH는 응용 프로그램의 메모리 사용량에 대한 스냅샷을 만들고, 각 할당에 사용된 스택을 기록하며, 여러 스냅샷을 비교하여 할당자에 대한 "누출된" 메모리 호출을 확인할 수 있습니다.또한 dbghelp.dll을 사용하여 스택 추적을 기호로 변환합니다.

UMDH보다 더 많은 메모리 할당자를 지원하는 "LeakDiag"라는 또 다른 Microsoft 도구도 있지만 찾기가 조금 더 어렵고 적극적으로 유지 관리되지 않는 것 같습니다.내 기억이 맞다면 최신 버전은 적어도 5년은 된 것입니다.

내가 당신이라면 로그를 가져와 다음과 같은 작업을 수행하는 빠른 스크립트를 작성할 것입니다(저는 Ruby로 작성했습니다).

def allocation?(line)
  # determine if this line is a log line indicating allocation/deallocation
end

def unique_stack(line)
  # return a string that is equal for pairs of allocation/deallocation
end

allocations = []
file = File.new "the-log.log"
file.each_line { |line|
  # custom function to determine if line is an alloc/dealloc
  if allocation? line
    # custom function to get unique stack trace where the return value
    # is the same for a alloc and dealloc
    allocations[allocations.length] = unique_stack line
  end
}

allocations.sort!

# go through and remove pairs of allocations that equal,
# ideally 1 will be remaining....
index = 0

while index < allocations.size - 1
  if allocations[index] == allocations[index + 1]
    allocations.delete_at index
  else
    index = index + 1
  end
end

allocations.each { |line|
  puts line
}

이는 기본적으로 로그를 통해 각 할당/할당 취소를 캡처하고 각 쌍에 대한 고유 값을 저장한 다음 이를 정렬하고 일치하는 쌍을 제거하고 무엇이 남아 있는지 확인합니다.

업데이트:중간 편집 죄송합니다(실수로 끝나기 전에 올렸습니다)

Windows의 경우 다음을 확인하세요.

MFC 메모리 누수 감지

나는 의 열렬한 팬이다 구글의 힙체커 -- 모든 누출을 포착하지는 못하지만 대부분의 누출을 포착합니다.(:모든 단위 테스트에 연결하십시오.)

첫 번째 단계는 어떤 클래스가 누출되는지 파악하는 것입니다.이를 알게 되면 누가 참조를 늘리고 있는지 확인할 수 있습니다.1.shared_ptr로 래핑된 클래스의 생성자에 중단점을 설정합니다.2.참조 카운트가 증가할 때 shared_ptr 내부에서 디버거를 사용하세요.변수 pn-> pi _-> use_count_ 표현식을 평가하여 해당 변수의 주소를 사용하십시오 (이와 같은 것 :& this-> pn-> pi_.use_count_), 주소 3을 얻게됩니다.Visual Studio 디버거에서 디버그->새 중단점->새 데이터 중단점...으로 이동합니다.변수 4의 주소를 입력하십시오.프로그램을 실행하세요.코드의 특정 지점이 참조 카운터를 늘리거나 줄일 때마다 프로그램이 중지됩니다.그런 다음 일치하는지 확인해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top