|
예전에 델마당에서 퍼둔 글입니다.
내용이 비슷한것 같아 올려둡니다.
------------------------
최근에 알게된 델파이 기본 메모리 관리자 특성에 관해 한가지 알려드리고자 합니다.
일반적으로 해당되는 이야기는 아니고, 멀티쓰레드 프로그래밍에 한하는 이야기 입니다.
일단, 같이 올린 예제에 관해 간단한 설명을 드리자면, 20개의 쓰레드를 생성한 후, 10개의 쓰레드에서는 메모리 블럭을 마구 할당하고, 다른 10개의 쓰레드에서는 할당된 블럭을 마구 FREE 하는 예제 입니다..
그런데, 이 프로그램을 실행 시키면 보통 수 분 내로 에러를 일으킵니다. 단독으로 실행시킬 경우는 걍 갑자기 소리소문 없이 퍽 꺼져버리고(-_-;;) 디버거로 잡으면 SysGetMem 함수에서 에러를 일으킨다고 나옵니다.
이 프로그램을 USE_COM_MM 을 define 하고 컴파일 하면 GetMem, FreeMem 대신에 CoTaskMemAlloc, CoTaskMemFree 를 사용하게 되는데, 이때는 아무리 오래 돌려도 프로그램이 죽지 않습니다.
결론부터 말하자면, 이 현상은 쓰레드의 스택사이즈를 너무 작게 잡았을때 나오는 현상 입니다. 예제에서는 스택 사이즈를 1KB 로 잡아 놓았습니다. 아마도, 델파이 메모리 관리자는 내부적으로 중첩된 GetMem 처리에 스택을 사용하는것으로 추측됩니다. (분석은 안해봤음--;)
이 현상은 TThread 상속하여 쓰레드를 만들었을때는 스택 사이즈가 시스템 디폴트로 설정되기 때문에 거의 마주칠 일이 없습니다. 그럼 뭐가 문제냐구요? 바로 VCL 이외의 다른 라이브러리에서 쓰레드를 생성하고, 델파이에서는 콜백 함수를 작성해야 하는 경우에는 문제가 됩니다...
예를들어, DirectShow 의 ISampleGrabber 의 콜백 함수에서 GetMem 호출이 있는 경우에, 위 현상이 아주 아주 가끔씩 나와서 속 뒤집어지게 만듭니다.... 아마도, 멀티미디어에 관련한 대부분의 콜백함수에 해당이 되지 않을까 생각이 됩니다... 보통 멀티미디어 API가 만드는 쓰레드들은 메모리 사용량을 최적화 하기위해 스택 사이즈를 작게 잡기때문이지요..
"스택사이즈가 작은 쓰레드 내에서는 GetMem, FreeMem 을 사용할때 조심해야 한다." 이 글의 요지 입니다.. (단순히 명시적 GetMem, FreeMem 만이 아니고, 스트링, 동적배열, 오브젝트생성 등 모든 부분에 GetMem FreeMem이 내부적으로 호출되기 때문에, 조심할 부분이 많습니다)
뭐 정보만 드려서는 소용이 없고, 뭔가 해결 방법이 있어야 될텐데... 제가 알아낸 방법은 현재 2가지 입니다. 콜백 함수 내에서는 CoTaskMemAlloc, CoTaskMemFree 만 사용하는 방법이 첫번째 입니다. 두 번째 방법은, 바로 FastMM 을 이용하는 것입니다. 테스트 해 본 결과, FastMM 을 이용하면 위 현상이 발생하지 않았습니다. (FastMM을 사용하실때는 FastMM4Options.inc 파일에서 AssumeMultiThreaded 를 반드시 활성화 시키셔야 합니다.)
첫번째 방법은 string 생성 등 간접적인 GetMem 호출을 막을 수 가 없으니 사용하기 어려운 방법 입니다..
두 번째 방법은, 여러모로 편리하고, 성능 향상도 있으니 좋기는 한데, 저도 아직 FastMM 을 오래 경험해본것은 아닌지라 강력하게 추천드리지는 못하겠습니다.
혹시, 제가 알지 못하는 위 현상에 관한 정보를 알고 계시거나, 다른 좋은 해결 방법을 가지신분, 혹은 위의 예제에 문제가 있는것을 발견하신 분은 꼭 댓글 부탁드리겠습니다. 예제로 검증한다고 하긴 했지만, 거의 추측과 경험을 바탕으로 쓴 것이라, 100% 옳다고 확신하지는 못하고 있습니다. (사실 위 해결 방법이라고 써 놓은것도 이틀정도 돌렸는데 죽지 않았다는 것일 뿐 다른 의미는 없습니다.)
아무쪼록 오늘도 알 수 없는 괴상한 현상들과 싸우고 있을 델파이 프로그래머분들께 유용한 정보가 되었으면 하는 바램입니다..
그럼 이만 :)
**************** 내용 추가 ****************
정답은 IsMultiThread 라는 전역 변수를 True 로 세팅하여 주는것 같습니다.
TThread 를 상속받은 클래스의 경우는 이 변수가 자동으로 True 로 셋업이 됩니다.
VCL 외부에서 만든 쓰레드 콜백, 혹은 CreateThread 로 직접 쓰레딩을 할때는 반드시 IsMultiThread 변수를 수동으로 True 로 설정하여 주어야 한다는 것이 답인것 같습니다.
위 예제를 4CPU서버에서 돌리면 시작과 동시에 바로 죽어 버리는데, IsMultiThread 변수를 True 로 하고 돌리면 전혀 이상이 없습니다.
결국 제대로 알지도 못하고 추측을 사실인것처럼 떠벌여 버렸습니다..죄송합니다..ㅠ_ㅠ
정답을 알려주신 프리맨님께는 다시한번 감사드립니다..
Hyoun 님이 쓰신 글 :
: 제가 Builder 4.0에서 쓰레드 객체를 이용하여 쓰레드64개가 동시에 돌아가는 프로그램을 만들었습니다.
: 근데 이 프로그램이 일반PC에서는 잘 돌아가는데 듀얼 프로세서를 사용하는 PC에서는
: 프로그램이 Exception에러를 범하거나 아예 흔적도 없이 죽어 버리는 일이 발생하였습니다.
: 자료를 찾아 보니 이런 경험을 하신 분이 더러 있긴 한것 같습니다만 딱히 방법을 찾을 수는 없어서
: 이렇게 글을 올립니다.
:
: 좋은 방법이나 이런 유사한 경험을 하신분의 내용을 공유하고 싶습니다.
:
:
: 더블어 Builder의 최신버젼을 알고 계신분은 답변 부탁 드립니다.
: 제가 6.0이 있는데 이 버젼은 혹시 듀얼에 대해 보안이 되었을까 해서요..
|