[c] malloc 후에 해방되지 않으면 어떻게 되나요?

이것은 오랫동안 나를 귀찮게했던 것입니다.

우리는 모두 학교에서 (적어도 나는) 배운 모든 포인터를 자유롭게해야한다는 것을 배웁니다. 그래도 메모리를 해제하지 않는 실제 비용에 대해서는 약간 궁금합니다. malloc루프 나 스레드 실행의 일부에서 호출 될 때와 같이 명백한 경우 에는 메모리 누수가 없도록 해제하는 것이 매우 중요합니다. 그러나 다음 두 가지 예를 고려하십시오.

먼저, 다음과 같은 코드가 있다면 :

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

실제 결과는 무엇입니까? 내 생각은 프로세스가 죽고 힙 공간이 사라져서 호출을 놓칠 때 아무런 해가 없다는 것입니다 free(그러나 어쨌든 폐쇄, 유지 보수성 및 모범 사례 를 위해 프로세스 를 갖는 것이 중요하다는 것을 알고 있습니다). 이 생각에 맞습니까?

둘째, 쉘처럼 작동하는 프로그램이 있다고 가정 해 봅시다. 사용자는 변수를 선언 할 수 있으며 aaa = 123나중에 사용할 수 있도록 일부 동적 데이터 구조에 저장됩니다. 분명히 * alloc 함수 (해시 맵, 링크 된 목록 등)를 호출하는 솔루션을 사용하는 것이 분명합니다. 이러한 종류의 프로그램 malloc의 경우 프로그램 실행 중에 이러한 변수가 항상 존재해야하며 정적 할당 공간으로이를 구현할 수있는 좋은 방법이 없기 때문에 호출 한 후에는 자유롭게 사용할 수 없습니다. 할당되었지만 프로세스 종료의 일부로 만 해제되는 많은 메모리를 갖는 것이 나쁜 설계입니까? 그렇다면 대안은 무엇입니까?



답변

거의 모든 최신 운영 체제는 프로그램이 종료 된 후 할당 된 모든 메모리 공간을 복구합니다. 내가 생각할 수있는 유일한 예외는 프로그램의 정적 저장소와 런타임 메모리가 거의 같은 팜 OS와 같은 것일 수 있으므로 해제하지 않으면 프로그램이 더 많은 저장소를 차지할 수 있습니다. (나는 여기서 추측하고 있습니다.)

따라서 일반적으로 필요한 것보다 많은 스토리지를 갖는 런타임 비용을 제외하고는 아무런 해가 없습니다. 확실히 당신이 제공 한 예에서, 당신은 지워질 때까지 사용될 수있는 변수에 대한 메모리를 유지하려고합니다.

그러나 더 이상 필요하지 않은 즉시 메모리를 비우고 프로그램 종료시 남아있는 모든 것을 비우는 것이 좋은 스타일로 간주됩니다. 사용중인 메모리를 파악하고 여전히 필요한지 여부를 생각하는 데 더 많은 연습이 필요합니다. 추적하지 않으면 메모리 누수가있을 수 있습니다.

반면에 종료시 파일을 닫으라는 비슷한 훈계는 훨씬 더 구체적인 결과를 낳습니다. 그렇지 않으면 파일에 기록한 데이터가 플러시되지 않거나 임시 파일 인 경우에는 그렇지 않을 수 있습니다 완료되면 삭제됩니다. 또한 데이터베이스 핸들은 트랜잭션을 커밋 한 다음 완료되면 닫아야합니다. 마찬가지로 C ++ 또는 Objective C와 같은 객체 지향 언어를 사용하는 경우 객체를 완료했을 때 객체를 해제하지 않으면 소멸자가 절대 호출되지 않으며 클래스가 담당하는 모든 리소스가 정리되지 않을 수 있습니다.


답변

예, 그렇습니다. 귀하의 모범은 해를 끼치 지 않습니다 (적어도 최신 운영 체제에서는 아닙니다). 프로세스에서 할당 한 모든 메모리는 프로세스가 종료되면 운영 체제에서 복구합니다.

출처 : 할당 및 GC 신화 (PostScript alert!)

할당 신화 4 : 가비지 수집되지 않은 프로그램은 항상 할당 된 모든 메모리를 할당 해제해야합니다.

진실 : 자주 실행되는 코드에서 생략 된 할당 해제는 누출을 증가시킵니다. 그들은 거의 받아 들일 수 없습니다. 그러나 프로그램이 종료 될 때까지 할당 된 메모리를 가장 많이 유지하는 프로그램은 종종 개입 해제없이 더 잘 수행됩니다. Malloc은 무료가 없다면 구현하기가 훨씬 쉽습니다.

대부분의 경우 프로그램 종료 직전에 메모리 할당 해제는 의미가 없습니다.
OS는 어쨌든 그것을 되 찾을 것입니다. 자유의지는 만져지고 사물을 페이징합니다. OS는 그렇지 않습니다.

결과 : 할당을 계산하는 “누설 감지기”에주의하십시오. 일부 “누수”가 좋습니다!

즉, 모든 메모리 누수를 피해야합니다!

두 번째 질문 : 디자인이 좋습니다. 응용 프로그램이 종료 될 때까지 무언가를 저장 해야하는 경우 동적 메모리 할당 으로이 작업을 수행해도됩니다. 필요한 크기를 미리 모른다면 정적으로 할당 된 메모리를 사용할 수 없습니다.


답변

=== 향후 교정코드 재사용은 어떻습니까? ===

당신이 경우 하지 않는 개체를 무료로 코드를 작성하면 폐쇄 프로세스에 의해 free되기를되는 메모리에 … 즉 작은 일회용 의존 할 때, 당신은 안전 인에 코드를 제한하는 것은 사용하기 프로젝트 또는 “throw-away” [1] 프로젝트) … 프로세스가 언제 종료되는지 알 수 있습니다.

당신이 경우에 () 모든 동적으로 할당 된 메모리를 무료입니다, 당신은 미래 코드를 교정하고시키는 것을 코드를 작성 다른 큰 프로젝트에서 그것을 사용할 수 있습니다.


[1] “throw-away”프로젝트에 관한 것. “Throw-away”프로젝트에 사용 된 코드는 버려지지 않습니다. 다음으로 10 년이 지났고 “throw-away”코드가 계속 사용되고 있습니다.

하드웨어가 더 잘 작동하도록 재미있게 코드를 작성한 사람에 대한 이야기를 들었습니다. 그는 ” 단지 취미는 크고 전문적이지 않다 “고 말했다. 몇 년 후 많은 사람들이 그의 “취미”코드를 사용하고 있습니다.


답변

당신은 정확하고 해를 끼치 지 않으며 종료하는 것이 더 빠릅니다.

이에 대한 여러 가지 이유가 있습니다.

  • 모든 데스크탑 및 서버 환경은 exit ()에서 전체 메모리 공간을 해제합니다. 힙과 같은 프로그램 내부 데이터 구조를 인식하지 못합니다.

  • 거의 모든 free()구현 메모리를 운영 체제에 반환 하지 않습니다 .

  • 더 중요한 것은 exit () 직전에 수행하면 시간 낭비입니다. 종료시 메모리 페이지와 스왑 공간이 해제됩니다. 반대로 일련의 free () 호출은 CPU 시간을 소모하여 디스크 페이징 작업, 캐시 누락 및 캐시 제거를 초래할 수 있습니다.

미래의 코드 재사용 가능성 에 관해서는 무의미한 작전 의 확실성 을 정당화합니다 . 고려 사항이지만 민첩한 방법 은 아닙니다 . 야 그니!


답변

나는 OP가 정확하거나 해가 없다고 말하는 모든 사람들에게 완전히 동의하지 않습니다.

모두가 현대 및 / 또는 레거시 OS에 대해 이야기하고 있습니다.

그러나 OS가없는 환경에 있다면 어떻게해야합니까? 아무것도없는 곳?

이제 스레드 스타일 인터럽트를 사용하고 메모리를 할당한다고 가정하십시오. C 표준 ISO / IEC : 9899에서 메모리 수명은 다음과 같습니다.

7.20.3 메모리 관리 기능

1 calloc, malloc 및 realloc 함수에 대한 연속 호출로 할당 된 스토리지 순서 및 연속성은 지정되지 않았습니다. 할당이 성공하면 반환 된 포인터는 모든 유형의 객체에 대한 포인터에 할당 된 다음 할당 된 공간에서 해당 객체 또는 이러한 객체의 배열에 액세스하는 데 사용될 수 있도록 적절히 정렬됩니다 (공간이 명시 적으로 할당 해제 될 때까지). . 할당 된 객체의 수명은 할당에서 할당 해제까지 연장됩니다. […]

따라서 환경이 당신을 위해 해방 일을하고 있다고 주어서는 안됩니다. 그렇지 않으면 마지막 문장에 “또는 프로그램이 종료 될 때까지”추가됩니다.

즉, 메모리를 비우는 것은 나쁜 습관이 아닙니다. 이식성이 없으며 C 준수 코드가 아닙니다. 적어도 ‘다음과 같은 경우 […], 환경에서 지원되는 경우’올바른 것으로 표시 될 수 있습니다.

그러나 OS가 전혀없는 경우 아무도 당신을 위해 일하고 있지 않습니다 (일반적으로 임베디드 시스템에서 메모리를 할당하고 재 할당하지 않지만 원하는 경우가 있습니다).

따라서 일반 C에서 (OP 태그가 붙은) 일반적으로 말하자면 잘못되고 이식성이없는 코드를 생성하는 것입니다.


답변

완료되면 모든 할당 된 블록을 해제합니다. 오늘 내 프로그램의 진입 점은 일 수 main(int argc, char *argv[])있지만 내일은 foo_entry_point(char **args, struct foo *f)함수 포인터로 입력 될 수 있습니다 .

그래서 그런 일이 생기면 이제 누수가 있습니다.

두 번째 질문과 관련하여 내 프로그램이 a = 5와 같은 입력을 받으면 a에 공간을 할당하거나 후속 a = “foo”에 동일한 공간을 다시 할당합니다. 이것은 다음까지 할당 된 상태로 유지됩니다.

  1. 사용자가 ‘unset a’를 입력했습니다
  2. 신호를 처리하거나 사용자가 ‘quit’을 입력하여 정리 기능을 입력했습니다.

프로세스가 끝난 후에 메모리를 회수하지 않는 최신 OS는 생각할 수 없습니다 . 그런 다음 다시 free ()가 저렴합니다. 왜 정리하지 않습니까? 다른 사람들이 말했듯이, valgrind와 같은 도구는 실제로 걱정해야 할 누출을 발견하는 데 좋습니다. 예를 들어 블록이 ‘여전히 도달 가능’으로 표시되어 있지만 누출이 없도록 할 때 출력에 추가 노이즈가 발생합니다.

또 다른 신화는 ” main ()에 있으면 해제 할 필요가 없다 “는 것입니다. 다음을 고려하세요:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

그것이 포크 / 데몬 이전 (이론은 영원히 실행되는) 이전에 온다면, 프로그램은 t 255 번 결정되지 않은 크기를 유출했습니다.

훌륭하고 잘 작성된 프로그램은 항상 자체적으로 정리해야합니다. 모든 메모리를 비우고, 모든 파일을 플러시하고, 모든 디스크립터를 닫고, 모든 임시 파일을 링크 해제하십시오. 충돌을 감지하고 다시 시작하십시오.

정말로, 당신이 다른 것들로 넘어갈 때 당신의 물건을 유지해야하는 가난한 영혼에게 친절하십시오.


답변

종료 할 때 메모리를 비워 두는 것이 좋습니다. malloc ()은 “힙”이라는 메모리 영역에서 메모리를 할당하며 프로세스가 종료되면 프로세스의 전체 힙이 해제됩니다.

즉, 사람들이 종료하기 전에 모든 것을 해제해야한다고 주장하는 한 가지 이유는 메모리 디버거 (예 : Linux의 valgrind)가 해제되지 않은 블록을 메모리 누수로 감지하고 “실제”메모리 누수가있는 경우 마지막에 “가짜”결과를 얻는다면 더 쉽게 찾을 수 없습니다.