source

C,C++의 메모리 누수, 무료, 삭제를 잊었습니다.

manysource 2023. 6. 9. 22:08

C,C++의 메모리 누수, 무료, 삭제를 잊었습니다.

우리는 malloc을 사용하여 C에 메모리를 할당하고 new를 사용하여 C++에 메모리를 할당합니다.할당된 메모리는 C에서 무료로 사용하거나 C++에서 삭제하여 OS로 돌려주어야 한다는 것을 알고 있습니다.메모리 할당 후 free/delete를 사용하는 것을 잊었다면 메모리 누수가 발생한다는 뜻입니다.

이제, 제 질문은 이 메모리 누수가 프로그램 실행 기간 동안에만 발생하는 것인지 아니면 영구적인 누수/손실이 발생하는 것인지 아니면 시스템을 다시 시작한 후에 다시 발생하는 것인지 여부입니다.실제로 내부 프로세스는 무엇입니까?메모리 누수/손실이 정확히 무엇을 의미합니까?

누군가 이것에 대해 자세히 설명해 주시거나 좋은 추천서를 주시면 감사하겠습니다.

업데이트 1

몇 가지 답변을 읽은 후 프로그램이 종료된 후 메모리가 OS/시스템에 반환된다는 것을 알게 되었습니다. 그렇다면 모든 사람이 메모리 누수에 대해 그렇게 신경을 써야 하는 이유는 무엇이며 메모리 누수를 방지하는 것이 매우 중요한 이유는 무엇입니까?

업데이트 2

따라서 메모리 누수를 방지하여 시스템이 할당을 위한 충분한 메모리 부족으로 인해 중단되지 않도록 해야 합니다.

업데이트 3

그래서 저는 모든 답을 읽고 메모리 누수가 시스템 충돌을 방지하는 데 중요한 문제라는 것을 깨달았습니다.하지만 저와 같은 초보자의 경우, 제 프로그램이 메모리 누수로부터 완전히 자유로운지 어떻게 확신할 수 있습니다.나는 무료로 하려고 노력하고, malloc을 사용하고 있다면 삭제하려고 노력하고, 새롭지만, 가끔은 지저분합니다.프로그램에서 메모리 누수가 발생하는지 알 수 있는 도구나 방법이 있습니까?

업데이트 4

답변을 읽은 후 메모리 누수가 없는 코드의 중요성, 신규/삭제 사용 감소, STL 사용 증가, RAII, Valgrind 및 우수한 프로그래밍 관행과 같은 새로운 사항을 알게 되었습니다.모두에게 감사합니다 :)

공정별입니다.프로세스가 종료되면 할당된 메모리는 다른 프로세스(새 프로세스 또는 기존 프로세스)에서 사용할 수 있도록 OS로 반환됩니다.

편집된 질문에 답하기 위해 컴퓨터에는 메모리가 한정되어 있습니다.따라서 메모리 누수가 있는 경우 다른 프로세스에서 메모리를 사용할 수 없다는 것이 가장 큰 문제입니다.무시할 수 없는 두 번째 효과는 프로세스 이미지가 증가하여 디스크로 전환하고 성능에 영향을 준다는 것입니다.마지막으로 프로그램은 시스템의 모든 메모리를 소진하고 자체적으로 메모리를 할당할 수 없기 때문에 실패합니다.

수명이 짧은 작은 프로세스의 경우 누출된 메모리의 양이 적고 수명이 짧기 때문에 메모리 누수는 허용 가능합니다.

필요한 것보다 더 많은 정보를 보려면 이 리소스를 살펴보십시오.여기서 논의하는 은 동적 할당 또는 힙 할당입니다.

메모리 누수는 단순히 애플리케이션이 할당된 메모리를 해제하지 못한다는 을 의미합니다.일단 프로그램이 종료되면 어떻게 되는지는 OS에 달려 있습니다.모든 최신 OS는 응용 프로그램에서 사용하는 모든 메모리를 회수하므로 프로세스가 종료되면 정리됩니다.

그러나 C/C++는 OS가 그렇게 한다고 보장하지 않습니다.일부 플랫폼에서는 재부팅할 때까지 메모리가 손실될 수 있습니다.

따라서 메모리 누수의 문제는 두 가지입니다.

  • 메모리를 재확보하기 위해 시스템을 재부팅해야 할 수도 있습니다.대부분의 플랫폼에서 이는 문제가 되지 않지만 일부 다른 리소스 유형이 유출되면 문제가 발생할 수 있습니다.
  • 프로그램이 실행되는 동안에는 절대로 확보하지 않는 메모리를 할당하므로 점점 더 많은 메모리를 사용하게 됩니다.프로그램을 장시간 실행할 경우 컴퓨터의 사용 가능한 메모리를 모두 사용하게 되고 그 후 프로그램이 중단될 수 있습니다.

많은 단기 실행 프로그램은 OS에 의해 곧 정리될 것이라는 것을 알고 있기 때문에 실제로 메모리 누수를 무시합니다.제가 알기로는 마이크로소프트의 C++ 컴파일러가 이를 수행합니다.그들은 컴파일러가 한번 호출되면 기껏해야 몇 분 동안 실행된다는 것을 알고 있습니다. (그리고 그들은 프로세스가 종료되면 OS가 메모리를 회수하는 Windows에서 실행된다는 것을 알고 있습니다.) 따라서 메모리가 여기저기 새는 것은 문제가 없습니다.

메모리 누수를 방지하는 방법에 대해서는 생성하지 마십시오.

새로 만들기/삭제를 사용할 때마다 메모리가 새는 위험이 있으므로 사용하지 마십시오.

데이터 배열이 필요한 경우 다음을 수행합니다.

std::vector<char> vec(200);

이 대신에:

char* arr = new char[200];

전자도 마찬가지로 효율적이지만 해제하기 위해 명시적으로 삭제를 호출할 필요는 없습니다.std::vector에서는 RAII를 사용하여 리소스를 내부적으로 관리합니다.그리고 당신은 같은 것을 해야 합니다. -- 같은 기성 RAII 클래스를 사용하거나.vector,shared_ptr또는 표준 라이브러리 또는 부스트에 있는 거의 모든 클래스 또는 자신의 클래스를 작성합니다.

일반적으로 코드는 할당 관리를 담당하는 클래스의 생성자/파괴자를 제외하고 새/삭제 호출을 포함하지 않아야 합니다.

개체가 생성자에서 필요한 메모리를 할당하고 소멸자에서 해제한 경우(그리고 복사/할당을 올바르게 처리하는 경우) 필요할 때마다 스택에 클래스의 로컬 인스턴스를 만들기만 하면 되며 메모리를 누출시킬 수는 없습니다.

C++에서 메모리가 새지 않는 비결은 new/delete를 호출하지 않는 것입니다.

운영 체제가 메모리를 추적하고 프로그램이 종료되면 모든 메모리를 회수합니다.이는 프로그램이 할당된 메모리를 추적하지 못했다는 것을 의미합니다.

일부 운영 체제에는 적용되지 않을 수 있지만 Windows/unix/mac 유형 시스템에는 적용됩니다.

참조: 메모리 누수를 감지하는 도구

Linux 기반 OS를 개발에 사용하는 경우 valgrind(http://valgrind.org/) 를 사용하여 메모리 누수를 감지할 수 있습니다.

valgrind --leak-check=full ./compiled_binary

디버깅 기호(예: gcc, -g 플래그 포함)를 사용하여 프로그램을 컴파일한 경우, valgrind는 누출된 메모리가 할당된 정확한 코드 줄도 알려줍니다.이렇게 하면 누출을 추적하고 수리하는 작업이 상당히 쉬워집니다.

찬성: 무료입니다.

반대: AFAIK, 그것은 리눅스에서만 작동합니다.

갱신하다

http://valgrind.org/info/platforms.html, 에서 볼 수 있듯이, Valgrind는 MacOSX, FreeBSD 및 NetBSD를 포함한 다른 OS(및 플랫폼)로 포팅되고 있습니다.

업데이트 2

(주제를 벗어났지만...)

발그랜드를 사용하는 것의 좋은 점은 단순히 메모리 누수를 확인하는 것 이상의 많은 일을 한다는 것입니다.http://valgrind.org/info/tools.html 을 참조하십시오.

빌드봇을 구성하여 모든 야간 빌드에 대해 발그랜드(및 부목)를 실행했습니다. 이는 매우 유용한 것으로 입증되었습니다!

메모리 누수를 감지하는 도구(예: 정제)가 있습니다.

C++ 프로그래머를 처음 접하는 사람으로서, 제가 드릴 수 있는 가장 좋은 조언은 당신이 작성하는 "새" 문과 "삭제" 문의 수를 최소화하는 방법을 배우는 것입니다.컴파일러가 객체를 스택에서 로컬로 생성하도록 하면 객체가 범위를 벗어나면 자동으로 삭제되는 메모리를 관리합니다.

RAI(Resource Acquisition Is Initialization)라는 프로그래밍 아이디어가 있습니다.이것은 "삭제해야 하는 메모리를 할당해야 하거나 열려 있는 메모리를 닫아야 하는 경우 스택에 만든 개체로 메모리를 감싼다"는 의미입니다.그러면 객체가 범위를 벗어나면 소멸자가 자동으로 호출되고 소멸자에서 리소스를 삭제할 수 있습니다."

일반적인 메모리 누수는 코드에 "새"를 쓸 때 발생하지만, 삭제를 호출하기 전에 함수가 종료됩니다.때로는 "반환" 문이 너무 일찍 발생할 수도 있고, "삭제" 문 뒤에 예외가 발생할 수도 있습니다.RAII를 준수하면 이러한 사고가 발생하지 않도록 보장할 수 있습니다.

메모리 누수입니다.

기본적으로 이 메모리는 프로세스가 파괴될 때까지 회수되지 않습니다.

문제는 포인터가 범위를 벗어나 메모리를 확보하지 않은 경우 프로세스에 의해 할당되지만 프로그램이 범위를 벗어나 더 이상 필요하지 않다는 것을 알 수 있는 방법이 없습니다(Valgrind와 같은 도구를 사용하지 않으면).

이것은 반복적으로 발생하는 경우에만 큰 문제입니다.그렇게 되면 프로그램이 실행되는 시간이 길어질수록 더 많은 메모리를 사용하게 되고 결국 충돌하게 됩니다.사용자는 이러한 상황이 발생하거나 너무 많은 시스템 리소스를 사용하지 않도록 정기적으로 응용 프로그램을 다시 시작해야 합니다.

업데이트 1:
한 번 실행하고, 실행하고, 종료하는 간단한 앱이 있다면 메모리 누수는 그다지 중요하지 않습니다.여전히 매우 나쁜 관행이며 코드 누출을 허용하는 코딩 스타일이라면 며칠, 몇 주 또는 몇 년 동안 작동하는 중요한 앱에 동일한 누출을 적용할 수 있습니다.우리는 여기에 유출되는 앱이 있어서 매달 다시 시작합니다.그것은 이상적인 상황이 아닙니다.

업데이트 2:
네, 거의 그렇습니다.그러나 메모리 누수는 단순히 버그이기 때문에 방지되어야 하며 버그가 허용될 수 있다는 관점으로 코드를 작성해서는 안 됩니다.

업데이트 3:
메모리 누수를 방지하는 가장 좋은 방법은 애초에 malloc/free를 사용하지 않는 것입니다.RAII에서 C++ 읽기를 사용하고 클래스를 사용하고 개체를 복사하면 누출이 발생하지 않습니다.거의 언제나메모리를 명시적으로 할당해야 하는 경우 메모리를 추적해야 합니다.포인터를 저장할 글로벌 개체가 필요하다는 의미인 경우 그렇게 합니다.포인터를 저장하는 컬렉션 클래스가 있음을 의미하는 경우 하나를 가져옵니다.잊어버릴 수 있는 로컬 변수에 메모리를 할당하지 마십시오. 무료 호출을 거치지 않고 함수에서 반환되거나 호출되지 않은 다른 함수로 전달될 수 있습니다.이를 위해 규율 의식이 필요하지만(많은 규율이 필요하지는 않지만), 많은 사람들이 좋고 정확하며 잘 설계된 버그가 없는 코드를 작성하기 위해서는 같은 미덕이 필요하다고 말할 것입니다(그리고 그들은 옳을 것입니다 - 잘 설계된 코드와 비교하여 함께 해킹된 코드를 본 적이 있다면, 그 차이를 즉시 알 수 있을 것입니다).

주의:
가비지 컬렉션 언어를 사용하더라도 메모리 누수가 발생합니다.사람들은 컬렉션에 개체를 추가한 다음 제거하는 것을 잊어버립니다. 그러면 개체는 영원히 기억에 남게 됩니다.그것은 새는 것으로 간주됩니다.사람들이 GC가 그들을 위해 모든 일을 할 것이라고 생각하기 때문에 GC 언어에서 이것을 하는 것은 상당히 흔한 일입니다.다시 말하지만 코딩/설계 규율이 필요합니다. 이러한 버그를 방지할 수 있는 작업을 알고 있어야 합니다.

malloc/new를 사용하지 않아도 메모리 누수가 발생할 수 있습니다.이미 일부 메모리를 가리키는 포인터 변수를 덮어씁니다.저의 가장 큰 유출원은 MSXML이었습니다. 저는 XML 객체의 CComPtr을 생성한 다음 메소드를 호출하여 요소를 가져와 메소드에 개체를 매개 변수로 전달했습니다.안타깝게도 클래스는 내부 포인터에 캐스팅되었으며 메소드는 새 포인터로 덮어쓰게 되어 이전 데이터가 유출되었습니다.여기서 교훈은, 만약 스마트 포인터 클래스를 사용한다면, 그것이 무엇을 하는지 확실히 알아야 한다는 것입니다. 특히 그것의 캐스트 연산자에 대해서 말입니다.

기능:
Windows에서 실행 중이면 Purify를 구입할 필요가 없습니다.Microsoft는 메모리의 스냅샷을 생성하는 UMDH를 제공합니다.2개의 스냅샷을 생성하고 툴을 사용하여 비교하면 할당이 해제되지 않고 시간이 지남에 따라 지속적으로 증가하는 할당을 확인할 수 있습니다.예쁘지는 않지만 효과가 있습니다.

추가할 몇 가지 사항:

  1. 처음부터 올바르게 일하는 법을 배우세요. 자유로운 기억력, 나쁜 습관을 고치는 것은 매우 어렵습니다.
  2. 메모리는 새로 만들기/삭제를 사용하여 관리하거나 관리해야 하는 고유한 리소스가 아닙니다.

    예를 들어, 일부 개체는 끝에 삭제해야 하는 일부 임시 파일을 포함할 수 있습니다.보통 그런 것들은 어떤 개체에 바인딩되어 있기 때문에 개체 삭제를 잊으면 파일 링크를 해제하는 것을 잊어버립니다.이 리소스는 시스템을 다시 시작해도 돌아오지 않습니다.

    파일, 소켓, 공유 메모리, 데이터베이스 연결 등 객체에 바인딩되고 새/삭제로 관리되는 유사한 리소스가 많이 있습니다.따라서 메모리를 관리하는 모든 기술은 사용해야 하는 제한된 다른 리소스를 관리하는 데 도움이 됩니다.

메모리는 손실되지 않지만 할당된 상태로 유지되므로 프로그램에서 수행하는 다음 할당에는 사용할 수 없습니다.즉, 프로그램이 할당 취소 없이 메모리를 계속 할당할 경우 메모리가 점점 더 많이 사용됩니다.잠시 후 할당되지 않은 메모리가 남아 있고 새 메모리를 할당하려는 다음 시도가 실패하여 프로그램이 할당됩니다.

이 메모리는 소위 "heap"에서 가져온 것입니다.프로그램에 로컬이며 프로그램이 완료되면 완전히 제거됩니다.따라서 프로그램이 시스템과 OS에서 실행 중인 다른 프로그램에 "유일한" 피해를 줄 수 있는 것은 프로그램이 모두 "먹었기" 때문에 메모리를 할당할 수 없다는 것입니다.프로그램을 종료하는 즉시 할당 문제로 인해 다른 프로그램이 중단되지 않았다면 다른 프로그램이 정상적으로 실행되어야 합니다.

메모리 누수를 모니터링하는 도구는 새 연산자와 삭제 연산자를 재정의하는 것입니다.이렇게 하면 할당되었지만 해제되지 않은 메모리 목록을 유지 관리할 수 있습니다.따라서 특정 개체가 사용 중인 모든 메모리를 해제해야 하는 경우 이 메커니즘을 통해 실제로 메모리를 해제했는지 확인할 수 있습니다.

퓨리파이와 함께, 당신은 자유로운 대안인 발그랜드를 시도할 수 있습니다.거기에 있는 규정은 발그랜드가 리눅스에 특화된 솔루션이라는 것입니다.

업데이트 3에 응답하기 위해서는 일반적으로 프로세스에 미결 메모리 할당이 있는지 여부를 나타내는 방법이 있습니다. Hheapwalk(Win32 아래)를 사용하면 모든 할당을 단계적으로 수행할 수 있으며 미결 메모리가 있는지 여부를 확인할 수 있습니다.

그 외에도 malloc/새 통화를 포장하여 각 할당의 파일과 라인 번호를 기록할 가치가 있습니다.그런 다음 재정의된 삭제/해제에서 목록에서 제거합니다.

예: (이 코드는 완전히 테스트되지 않은 코드이므로 즉시 작동하지 않을 수 있음을 경고합니다.

struct MemoryAllocEntry
{
    char* pFile;
    char* pLine;
};

extern std::map< MemoryAllocEntry > g_AllocList;

inline void* MyMemAlloc( size_t size, char* pFile, char* pLine )
{
    MemoryAllocEntry mae;
    void* pRet = malloc( size );
    mae.pFile = pFile;
    mae.pLine = pLine;

    g_AllocList[pRet] = mae;

    return pRet;
}

inline void MyMemFree( void* pPtr )
{
    std::map< MemoryAllocEntry >::iterator iter = g_AllocList.find( pPtr );
    if ( iter != g_AllocList.end() )
    {
         g_AllocList.erase( iter );
    }
    free( pPtr );
}

#ifdef _DEBUG
    #define malloc( x ) MyMemAlloc( (x), __FILE__, __LINE__ )
    #define free( x ) MyMemFree( (x) )
#endif

그런 다음 g_AllocList에서 미결 할당을 찾기만 하면 됩니다.위의 내용은 분명히 malloc 및 free에서만 작동하지만 신규 및 삭제에도 작동할 수 있습니다(예를 들어, MFC에서 작동함).

질문에 대한 답변과 업데이트 1:

모든 운영 체제가 별개의 프로세스 개념을 지원하는 것은 아니므로 자동으로 정리되지 않습니다.

예를 들어 VxWorks와 같은 내장 OS(일부 구성)는 여러 프로세스의 개념을 지원하지 않으므로 작업이 종료된 후에도 할당 취소에 실패한 메모리는 그대로 유지됩니다.의도하지 않았다면 메모리 누수가 발생할 수 있습니다.

또한 이러한 플랫폼은 거의 재부팅되지 않고 스왑을 지원하지 않는 시스템에서 사용되므로 데스크톱보다 메모리 누수가 훨씬 더 심각합니다.

메모리 누수를 방지하는 올바른 방법은 메모리 관리를 덜 명시적으로 수행하고 C++의 STL(관련 부분)과 같은 항목을 관리하는 컨테이너에 의존하는 것입니다.

C를 사용하는 낮은 수준의 임베디드 프로그래머는 시작 시 모든 것을 정적으로 할당하여 메모리 누수를 방지하는 경우가 많습니다.또한 C 프로그래머는 가능한 메모리 누수를 방지하기 위해 alloca()와 함께 스택 할당을 사용할 수 있습니다.

그것은 성공에 할당된 메모리입니다.당신이 성공을 거두면 그것을 돌려받을 것입니다.

메모리 누수는 일반적으로 장시간 실행되는 프로그램에서 문제를 야기합니다. 루프와 같은 잘못된 위치에서 몇 바이트만 유출하면 응용 프로그램의 메모리 설치 공간이 빠르게 확장될 수 있습니다.

편집에 응답 -

만약 당신의 프로그램이 실행되도록 되어 있다면, 무언가를 실행한 후 종료될 것이라면, 아니요, 메모리를 확보하는 것에 대해 너무 걱정할 필요가 없습니다.잠시 동안 실행되는 프로그램의 경우 중요합니다.웹 브라우저에서 페이지를 표시하는 데 사용하는 메모리를 확보하지 않으면 곧 컴퓨터의 모든 메모리를 사용하게 됩니다.

어쨌든 메모리를 자유롭게 하는 것은 좋은 연습이지만, 한 번 실행되는 작은 프로그램은 다른 것으로 바뀌는 습관이 있고 들어가는 것이 좋은 습관입니다.

프로그램이 창에서 끝나면 메모리뿐만 아니라 모든 핸들이 열립니다(잘못되면 수정).

언급URL : https://stackoverflow.com/questions/1232262/memory-leak-in-c-c-forgot-to-do-free-delete