[pascal] Setlength와 레퍼런스 카운팅

pascal에는 SetLength라는 함수 가 있다.

함수에 하이라이팅을 한것은, pascal에는 프로시저,함수,메소드,프로퍼티 이 4가지를 구분할 수 있어야하기 때문이다.

C언어함수만이 존재하고,(반환값이 void 여도 함수라고 한다.)

C++함수메소드 모두 존재하고, Java메소드만 존재한다.

여튼, SetLength는 C++new와 비슷한 함수이다.

다만 메모리 누수를 막기위해 C++은 반드시 delete 를 명시해야 했지만,

pascal 은 그렇지 않다. C++ 유저도 들어봤을 법한 레퍼런스 카운팅을 사용한다.

nil을 대입하거나, 새로 할당할 경우 기존의 할당한 바이트덩어리는 해제된다.

간단하게 아래의 소스를 보자.

program heap_test;
uses
  SysUtils;{GetFPCHeapStatus}
var
  a,b:array of longint;
  heap:TFPCHeapStatus;
begin
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  SetLength(a,5);
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  SetLength(a,5);
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
end.

output

Curr Heap Used : 304
Curr Heap Used : 336
Curr Heap Used : 336

첫번째야 그렇다 치고, 두번째는 336바이트인데, 우리는 longint(4byte) 5개를 할당했으니 20byte가 늘어나야 하는데, 실제론 32byte가 늘어났다.

이는 음수 오프셋에 추가 정보를 담고 있어서 그렇다. 12byte의 추가 정보가 있는데, delphi 공식 문서에서의 설명은 아래와 같다.

동적 배열 메모리 레이아웃

???

설명에서는 추가적으로 8byte를 사용하는데 실제론 왜 12byte인지는 잘 모르겠다.

어쨋든 offset -8에 속한 참조 카운트가 0이 되면 메모리를 해제하는 방식이다.

program heap_test;
uses
  SysUtils;{GetFPCHeapStatus}
var
  a,b:array of longint;
  heap:TFPCHeapStatus;
begin
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  SetLength(a,5);
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  a:=nil;
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
end.

output

Curr Heap Used : 304
Curr Heap Used : 336
Curr Heap Used : 304

이렇게 nil을 대입하므로 메모리 해제를 할 수 있다.

반면 아래와 같이 a를 b에 옮겨두면 a에 nil 을 대입해도 메모리 해제가 이루어 지지 않는다.(레퍼런스 카운터는 유지되기 때문)

program heap_test;
uses
  SysUtils;{GetFPCHeapStatus}
var
  a,b:array of longint;
  heap:TFPCHeapStatus;
begin
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  SetLength(a,5);
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
  b:=a;
  a:=nil;
  heap:=GetFPCHeapStatus;writeln('Curr Heap Used : ',heap.CurrHeapUsed);
end.

output

Curr Heap Used : 304
Curr Heap Used : 336
Curr Heap Used : 336

C/C++에 비해 좀더 자유로운 동적 배열 관리지만, 익숙하지 않기도 하다.

만일 레퍼런스 카운팅을 한 변수가 함수나 프로시저의 스택영역에 있다가 사라져도 레퍼런스 카운팅은 내려간다.