프로그램이 끝날때까지 살아있는 객체의 동적할당

klara의 이미지

보통 프로그램이 끝나면 동적할당된 메모리들도 다 운영체제에 의해서 회수되는 걸로 알고 있습니다.

그렇다면 어떤 객체를 동적할당으로 생성하되, 예를 들어 이 객체가 싱글톤이 적용되어있어서 단한번만 생성되고,
프로그램이 종료될때까지 살아있는 경우라면, 따로 메모리해제 코드를 넣지 않아도 괜찮을까요...?

chadr의 이미지

이론적으로는 그렇습니다.

하지만 객체가 해제될때 뭔가를 해야한다거나 한다면 반드시 명시적으로 해제를 해주셔야합니다.

명시적으로 해제하지 않은 메모리를 운영체제가 회수하는 시기는 명확치 않기 때문입니다.

ps. 그래도 프로그램의 깔끔함과 명료함을 위해서 가능한 모든 메모리는 프로그래머가 직접 할당/해제를
책임져주는걸 강추합니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

jick의 이미지

위엣 분 말씀처럼 객체가 해제될 때 뭔가 시스템에 영향을 미치는 일을 (임시 파일을 지운다든지...) 해야 한다면 당연히 명시적으로 해제를 해주셔야 합니다만,

그렇지 않은 경우라면 전혀 해제할 필요 없습니다. 프로세스가 끝나는 순간 그 프로세스에 속한 메모리 영역과 (거의) 모든 리소스는 OS가 깨끗이 청소해 버립니다. 어차피 OS가 날릴 건데 프로그램이 일부러 날려 주겠다고 끝나기 직전에 열심히 destructor를 부르는 건 성능 측면에서 봤을 때 완전히 시간 낭비입니다. 내일 철거할 집 오늘 대청소하는 꼴이죠.

뭐, 사람에 따라서는 미적인 측면에서 선호하는 경우도 있습니다만, 저는 그 짓을 하겠다고 코드를 더 걸레로 만드는 꼬락서니를 여러 번 봐서... -_-;;;

chadr의 이미지

요즘 나오는 운영체제는 대부분 알아서 회수를 해주지만..
만약에 그 코드를 그렇지 않은 환경에서 사용할려고 할때는 문제가 심각해집니다.

성능이라는건 일반적인 런타임시 측정되는게 성능이라고 할수 있겠는데 굳이 프로그램 종료 몇백분의 일초 줄일려고
코드를 생략하는것 보다는 명시적으로 호출을 해주면 추후에 그 코드를 보는 사람도 어디서 정확히 객체가 해제된다는걸
따로 물어볼 필요도 없이 코드만 보고도 알수 있습니다.

만약에 해제코드가 없이 그냥 둔다면 추후에 그 코드를 유지보수 하는 사람은 혹시 있을지 모르는 해제 코드를 찾기 위해서
모든 소스코드를 뒤지면서 "이런!!!"을 외칠지도 모릅니다. :)
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

jick의 이미지

뭐 "그렇지 않은 환경"에서 사용하거나 앞으로 사용될 가능성이 있는 코드라면 당연히 chadr 님 말씀이 맞겠죠.

그런데 절대 그런 환경에서 쓸래야 쓸 수 없는 코드를 짜면서도 그걸 모르고 "이거 프로세스 종료하기 전에 해제해야 하는 거 아니야?"라고 생각하는 사람들이 꽤 되더군요.

* 뭐, 종료하기 전에 해제할 필요가 없다는 사실을 알지만 그래도 해제하는 것이 더 깔끔하다고 생각하는 분들에게는 불만 없습니다. 그건 취향의 문제니까. 다만 저는 런타임 성능보다도, 티끌 모아 태산이라고 "일일이 찾아다니며 해제하는 코드"도 모으면 한두 줄이 아닌데 그걸 짜고 있는 건 프로그래머의 코딩 시간 낭비처럼 느껴집니다. 특히 프로그램의 exit path가 한 곳이 아니면 더더욱 골치아파지죠.

maximus의 이미지

프로그램을 나 혼자 하는 상태이면 모를까 프로그램 성격에 따라 모듈 식으로 짜맞춰야 할때가 분명 생깁니다.

그런 프로그램의 경우 스탠드얼론으로 동작되는 프로그램을 메인 프로그램에 삽입하는 경우가 생기게 되는데, 이 경우 해제를 안하고 쓰던 소스인 경우 일일이 다 검토를 해야 하는 경우가 생깁니다. 근래에 jpeg 관련된 소스를 포팅하게 되었는데 그거 만든 친구가 그런식으로 해제를 안하고 만들어서 결국 그 소스 전체를 다 뒤지고 분석해서 일일이 해제를 해줘야 했었습니다.
갖다 쓰는거니 당연히 해야 한다고 볼수도 있지만 이러한 습관 하나 하나가 향후에 미치는 영향은 대단합니다.

짠밥이 먹으면 먹을수록 느끼는것들이 참 많습니다만,
그 중에 하나가 메모리 할당 해제에 관한 부분입니다.

어떤 코딩이라도 "좋은 코딩"은 있게 마련입니다.

저 역시 당연히 해제를 해주고 종료토록 해야 한다고 생각합니다.

=================================
:: how about a cup of tea ? ::
=================================

=================================
:: how about a cup of tea ? ::
=================================

atomaths의 이미지

프로그램의 exit point를 일관되게 유지하는 것이 모듈화측면에서도 더 좋겠고, 오해의 소지가 있는 부분은 명시적으로 기록해둬서 기계가 이해하는 코드가 아닌 사람이 이해하는 코드로 만드는 것도 나쁘진 않을 것 같습니다.

프로그램이 자체적으로 메모리 해제를 하는 것과 OS가 하는 것은 결과적으로 똑같은 CPU clock을 거치게 될테니까 결코 낭비는 아니라고 생각됩니다만...

bootmeta의 이미지

명칭을 따지자면 signleton manager라고 해야할까요.

사용 signleton을 manager에 등록해놨다가 프로그램 종료시에 해제되지 않은 signleton들을 검사해 한번에 release해 줍니다.

돌아다니는 라이브러리도 많지만 사용자가 만들기에도 부담스럽지 않을정도로 간단합니다.
물론 사용 역시 복잡하지도 않고 프로그램 종료 시 한번 call하면 됩니다.

c,c++에서 메모리 관련한 작업은 최대한 방어적으로 짜야 합니다.
toy 프로그램이 아닌 이상 미적인 측면이나 취향의 문제가 아니라 권장 사항입니다.
(물론 그 것이 recommended인지 required인지 상황에 따라 다를 수는 있습니다.)

체스맨의 이미지

chadr 님 말씀에 동의하면서, 한가지 더 말씀드리면, 메모리 누수를 검출해야 하는데 만일 해제 되지 않는 메모리들이 남아있다면, 그 메모리가 실행 중 누수되는 것인지 아니면, 종료시 커널에 반납되는 것만으로 별다른 문제가 없는 것인지 판단하는데 어려워지겠죠.

Orion Project : http://orionids.org

jick의 이미지

역시 어떤 툴을 이용하여 메모리 누수를 찾고, 어떤 정책을 세우느냐의 문제이겠습니다만...

Linux에서 대단히 널리 사용되는 툴인 valgrind의 예를 들자면, 전역변수에서 시작하여 포인터를 따라갈 수 있는 영역과 그렇지 않은 영역을 구분하여 전자는 reachable, 후자는 memory leak로 구분하여 표시합니다.

예를 들어 전역변수 void *p를 선언한 이후 p = malloc(1000); 하고 나서 free를 부르지 않고 종료했다 하더라도 이 메모리는 전역변수로 따라갈 수 있으므로 통상적인 의미의 leak이 아님을 판정할 수 있습니다. (물론 정책에 따라서 이런 것도 leak으로 간주하겠다고 할 수도 있습니다만...)

또한 프로그램 종료 직전에 free를 부르는 것도 항상 도움이 되지는 않는 것이, 예를 들어 객체 내부에 linked list 같은 게 들어있어서 크기가 bound되지 않는 경우, 부주의하게 짜면 리스트 길이가 마구 늘어나 메모리를 사정없이 잡아먹을 수 있습니다. 하지만 이런 경우 종료시에 linked list를 따라가면서 지워주는 루틴을 짜는 건 별로 어렵지 않은 일이기 때문에 종료 직전에 이 루틴을 불러주고 free 유무만으로 leak 검사를 할 경우 (실질적인 의미에서 분명 memory leak과 다름없음에도 불구하고) 감시망을 벗어나게 됩니다. 이런 경우 차라리 free를 불러주지 않았다면 valgrind 같은 툴로 쉽게 문제를 발견할 수 있을 텐데 말이죠. (극단적인 예가 아니냐고 하시겠지만 회사에서 비슷한 상황을 여러 번 보았습니다.)

다시 말씀드리지만 최소한의 memory protection을 지원하는 OS에서 프로세스 종료 직전에 메모리를 free/delete하는 것은 성능상 아무런 이점이 없으며, 경우에 따라(!) 잠재적인 memory leak을 발견하는 데 도움을 줄 수 있을 뿐입니다.

제 경험이 절대적인 것은 아니니, 다른 분들은 실제로 이런 "무조건 free 정책"으로 디버깅에 효과를 보시는지도 모르겠습니다. 다만 저는 아직까지 그런 사례를 겪은 적이 없다는 말씀을 드리고 싶네요.

체스맨의 이미지

뭐, 제 경험과는 반대의 경험을 하셨으니 경험에 대해서는 존중합니다만,

저는 원칙적인 말씀을 드린 것이고, 원칙적으로는 free 를 하는 게 맞습니다. 그래야만 leak 을 검출하는데 도움이됩니다. 메모리 protection 을 지원하는 모든 운영체제에서 valgrind 를 실행할 수 있는 것을 보장할 수 있습니까?

또한, malloc_hook이나 ms 의 crtdbg 를 이용해서 별도의 툴을 통하지 않고도, 종료시 누수된 부분만 print 하는 코드를 넣음으로서, 코드가 수정되고 발전을 거듭하는 도중에 발생하는 leak을 손쉽게 알아차리고 대응할 수가 있는 것입니다.

Orion Project : http://orionids.org

jick의 이미지

관련 주제로 찾아보니 다음과 같은 페이지가 있군요. 뻔한 얘기지만 한번쯤 읽어 볼만할듯.

http://c-faq.com/malloc/freeb4exit.html

http://c-faq.com/malloc/freeb4exit2.html

체스맨의 이미지

문서를 읽고 어떤 의견을 참조하라는 의미인지는 잘 모르겠지만, jick 님께서 위에서 하신 주장의 근거를 말씀하시려는 거라면, 글쎄요... 우선 거기서 말하는 '반드시 해제해야되는 건 아니다.' 라는 건 말 그대로 반드시 그런 건 아니라는 것일 뿐입니다. 이건 시스템 특성에 관련된 얘기고 여기에대해서는 저도 이견이 없습니다만,

처음 문서의 다음 부분을 참조하실 필요가 있을 것 같고, 제가 '원칙적'이라고 말씀드린데 대한 근거가 될 것입니다.

<span>In any case, it can be considered good practice to explicitly free all memory</span>--for example, in case the program is ever rewritten to perform its main task more than once (perhaps under a Graphical User Interface). 

두번째 문서에

Under some other circumstances, it is clearly more trouble than it's worth to free memory -- if a program has lots of complicated, interconnected, dynamically-allocated memory structures, and if the program is about to exit, going to great lengths to free all the memory might add nothing more to the program beyond a bigger code-space footprint, a slightly longer running time, and a higher probability of bugs.

여기서도, 'Under some other circumstances'라는 점을 감안해야 합니다. 복잡한 자료구조를 해제하는데 버그를 유발할 가능성이 높아서 해제하지 않는다는 것은, 어떤 경우 잠재적 버그를 남긴다는 것을 의미합니다. 만일 그런 복잡한 자료구조를 갖는 개체가 실행 중 빈번히 생성되고 소멸되어야 한다면 분명히 버그를 유발하지 않는 적절한 해제 루틴을 가져야 합니다.

그리고 두번째 문서의 다음 부분도 참고할만 합니다.

If your program is a long-running server which never exits, you must free memory as you're finished with it, otherwise all available memory will eventually ``leak away''.

결국 두 문서는 장기간 실행되는 서버나, 기능이 반복적으로 실행되는 GUI 프로그램 등과 같은 경우는 반드시 해제를 권유하고 있습니다.

두번째 문서의 다음 부분 정도에 해당하는 프로그램이라면, 저도 해제하지 않고 남겨두는데 찬성하겠습니다.

In a program, on the other hand, which does one task (though perhaps a long, complicated task) and then exits, which allocates a lot of dynamic memory in the process of performing that task, and which needs the majority of the memory for the duration of the processing of the task, trying to explicitly free it all would be a waste of time, and in fact it could be advisable (from the standpoints of programming time, code size, run time, and reliability) not to make the attempt.

Orion Project : http://orionids.org

jick의 이미지

음... 약간 제가 오해의 소지를 남긴 것 같은데, 저는 저 글이 제 주장을 100% 뒷받침해서가 아니라 나름대로 균형잡힌 시각으로 글을 잘 썼다고 생각해서 링크를 건 것입니다. 그러므로 "이게 네 주장과 동일한 건 아니지 않느냐?"라고 반문하신다면, 맞습니다.

하지만 이 부분은 오독하신 것 같은데요.

Quote:
Under some other circumstances, it is clearly more trouble than it's worth to free memory -- if a program has lots of complicated, interconnected, dynamically-allocated memory structures, and if the program is about to exit, going to great lengths to free all the memory might add nothing more to the program beyond a bigger code-space footprint, a slightly longer running time, and a higher probability of bugs.

이 이야기는 분명히 "이런 경우에 메모리를 free 안하고 그냥 종료하면 아무런 문제도 없을 것을 불필요한 free를 하느라 버그가 생길 수 있다."라는 뜻입니다. 우리말로는 "긁어 부스럼"이라고 하죠. Free를 하지 않으면 잠재적인 버그를 남긴다는 뜻이 아닙니다.

만일 복잡한 자료구조를 갖는 개체가 실행 중 빈번히 생성/소멸된다면... 물론 당연히 제대로 된 free 루틴이 있어야죠. 그건 너무나 당연합니다. 하지만 지금 문제가 되는 상황과는 별개의 얘기죠.

이렇게까지 쓰고 있자니 사람이 추해지는 것 같지만 -_- 구태여 부연하자면, 다음 부분도 왜 제 의견에 대한 반박이 된다고 생각하시는지 잘 모르겠습니다.

Quote:
If your program is a long-running server which never exits, you must free memory as you're finished with it, otherwise all available memory will eventually ``leak away''.

이 이야기는 "해당 메모리 영역이 필요가 없어지면" (as you're finished with it) 바로 free를 부르라는 뜻이지, 프로그램 종료시에 부르라는 말이 아니죠. 즉 예를 들어 "서버가 뜰 때 할당해서 서버가 도는 동안 항상 필요한 메모리 영역이 있다면 프로그램 종료 시점에 free를 불러야 하느냐?" 라는 질문에 대해 "그렇다."라고 말하는 문장이 아니라는 겁니다.

더 이상 마이너스 포인트를 수집하기 전에 이제 그만해야겠네요. 더 이상 이 쓰레드에는 글을 쓰지 말아야겠습니다. -_-;;

체스맨의 이미지

예, 그래서 "문서를 읽고 어떤 의견을 참조하라는 의미인지는 잘 모르겠지만" 이라는 전제를 남겼습니다.

"이 이야기는 분명히 "이런 경우에 메모리를 free 안하고 그냥 종료하면 아무런 문제도 없을 것을 불필요한 free를 하느라 버그가 생길 수 있다."라는 뜻입니다. "

그 부분을 오독한 적은 없습니다. 그 부분이 불필요하다라는 판단을 하려면, 특수한 조건 예를 들어 제 윗글에 맨 마지막 부분에 인용한 것과 같은 프로그램의 특성에 대한 조건이 필요합니다. 그래서, 이미 말씀 드렸듯이, 맨 처음에 전제하는

Under some other circumstances

이 부분에 주목해야 한다는 얘깁니다. 제가 말씀드린 핵심은, '원칙적'이라는 얘깁니다. 어떤 상황에서는 이렇고 라는 전제가 아니라요.

즉 예를 들어 "서버가 뜰 때 할당해서 서버가 도는 동안 항상 필요한 메모리 영역이 있다면 프로그램 종료 시점에 free를 불러야 하느냐?" 라는 질문에 대해 "그렇다."라고 말하는 문장이 아니라는 겁니다.

제가 그런 질문에 그렇다 라고 말하는 문장이라고 얘기했는가요?

어떤 조건이 전제가 되어서, 그 조건에는 그렇게 되고, 저렇게 되고... 그런 상황은 당연히 있을 수 있습니다만, 원칙은 첨부하신 내용에도 있듯이

In any case, it can be considered good practice to explicitly free all memory--for example, in case the program is ever rewritten to perform its main task more than once (perhaps under a Graphical User Interface).

이것이고, 다수의 개발자가 참여하는 복잡한 프로그램에서 이건 하나의 원칙이라고 봐야 할 것입니다.

말씀 드렸듯이, '해제를 반드시해야하는 것은 아니다.' 이것에는 이견이 없습니다. 이것을 주장하신 것인데 전달하는 도중에 오해나 문제가 있었다는 것을 말씀하시는 것인지 모르겠군요.

Orion Project : http://orionids.org

체스맨의 이미지

한가지 더 첨언하면, 지금 당장에는 프로그램 실행시 상주해서 굳이 종료시 해제가 불필요한 메모리라 판단되더라도, 프로그램이 수정되어 구조적인 변형, 다른 모듈에서 호출하게 된다던가, 새로운 모듈의 일원이 되는 등의 수정이 가해지면, 그 당시의 판단은 그릇된 것이 될 수 있고, 그런 판단하에 작성됐던 코드들은 잠재적 문제를 안을 수도 있다는 것입니다.

그래서 현재 그리고 미래의 유지 보수까지 생각하면, 원칙적으로는 해제해야 맞습니다.

Orion Project : http://orionids.org

klara의 이미지

많은 분들의 답변 감사합니다.

일단은 그리 어려워보이지 않아서 bootmeta 님의 말씀대로 이런 객체들이 생성될때 등록하게 한담에 메인 루프가 끝나면 등록된 포인터들을 지우는 식으로 구현해봤습니다.

생성된 객체의 포인터를 등록할때, 모든 객체를 다음수 있도록 void *로 등록하게 하면 delete가 불가능하니까, 싱글톤 클래스들을 위한 클래스를 만들고 이걸 상속받게 한담에 객체를 생성할때 포인터를 등록하도록 하였습니다.

h 파일
class Singleton {
public:
	virtual ~Singleton() {}
	static void deleteAll() {
		for (int i=0; these.size(); ++i)
			delete these[i];
		these.clear();
	}
protected:
	Singleton(Singleton *thisPtr) {these.push_back(thisPtr);}
private:
	static std::vector<Singleton *> these;
};
 
cpp 파일
std::vector<Singleton *> Singleton::these = std::vector<Singleton *>();

이렇게 짠후에 메인 루프가 끝나면 Singleton::deleteAll()을 불러주면 되겠지...라고 생각했는데, 프로그램이 종료할때 segmentation fault가 발생합니다.
디버깅해보면 역시나 원인은 Singleton::deleteAll()이었는데 뭐가 잘못되었는지 모르겠네요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.