C C++

C/C++ 가비지 컬렉션 라이브러리 libgc 사용하기

_침묵_ 2006. 6. 30. 22:29
 
 
C/C++ 가비지 컬렉션 라이브러리 libgc 사용하기
Mukesh Kapoor
 
사용자 삽입 이미지
이 글은 메모리 관리와 관련된 런타임 에러들에 대해 다루고 가비지 컬렉션 라이브러리인libgc를 이용해 이러한 문제를 해결 하는 방법을 다룹니다. 대부분의 경우 코드를 변경하지 않은 채 사용자의 코드에 단순히 라이브러리만 링크하여도 에러는 수정 될 것입니다. 라이브러리의 API에서 사용 가능한 함수를 호출하여 추가적인 이점을 얻을 수도 있습니다. 라이브러리는 C API를 가지고 있으므로 사용자는 C 혹은 C++ 프로그램에서 둘다 이용 가능합니다.libgc는 썬 C++ 컴파일러 제품에 포함되어 있습니다.
Contents
 
가비지 컬렉션이란?
libgc라이브러리가 하는 일은?
일반적인 메모리 문제들
libgcAPI
Modes of Operation
요약
 
가비지 컬렉션이란?
사용자 삽입 이미지

가비지 컬렉션이란 동적 메모리를 자동적으로 관리하는 것을 말합니다.libc함수인malloc()free()을 사용하거나 C++의 연산자인newdelete을 사용해서 메모리를 할당하고 다시 제거할 수 있습니다. 동적메모리 관리는 복잡하고 에러가 많이 발생하는 경향이 있으므로 자주 일어나는 런타임 에러의 원인 중 하나입니다. 이런 에러들은 때때로 프로그램의 실행을 중단시키며 찾기도 어렵고 해결하기도 난해합니다.

가비지 컬렉터는 프로그램의 데이터를 자동으로 분석하여 메모리가 사용되고 있는지, 메모리를 언제 시스템에 반환할 것인지 결정합니다. 메모리가 더 이상 사용되지 않는다고 판단되면, 가비지 컬렉터는 메모리를 반환합니다.

libgc라이브러리는 메모리 관리를 쉽게 할수 있도록 도와줍니다. 또한libgc라이브러리는 프로그램내에서 사용된 다른 라이브러리와 관련된 메모리 문제 또한 자동으로 해결해 줍니다.

 
libgc라이브러리가 하는 일은?
사용자 삽입 이미지

libgc라이브러리는 주기적으로 프로그램의 데이터와 모든 heap 객체들의 쓰임을 확인하는 보존적인 가비지 컬렉터입니다. 포인터를 통하여 도달가능한 모든 heap 객체들에 대한 표시가 완료되면, 가비지 컬렉터는 표시된 모든 객체들을 반환합니다. 마지막 가비지 콜렉션 이후에 할당된 메모리들의 양을 모니터링 함으로써 가비지 컬렉션의 시기가 결정됩니다. 많은 양의 메모리가 할당되었을 때, 가비지 컬렉션이 일어납니다.

이런 방법으로 프로그램은 불필요한 가비지 컬렉션에 많은 시간을 쓰지 않기 위해 동적 메모리를 많이 사용하지 않습니다. 라이브러리는 내부에 효율적인 메모리 할당기를 가지고 있습니다.

Note-
libgc라이브러리와 외부의 메모리 할당기를 함께 사용하지 마십시오. 라이브러리는 내부 메모리 할당기와 함께 최대의 성능을 발휘합니다. 다른 메모리 할당기가 할당한 메모리는 자동으로 관리하지 않습니다. 다른 메모리 할당기 사용시,libgc는 가비지 컬렉션을 위해 메모리의 포인터를 찾지 않습니다.
 
 
일반적인 메모리 문제들
사용자 삽입 이미지

libgc라이브러리 사용시 발생하는 메모리 관리와 관련된 문제들은 크게 다음과 같습니다.

  • 메모리 부족
  • 너무 이른 메모리 해제
  • 메모리 단편화

그러나,libgc라이브러리는 메모리 덮어쓰기 때문에 문제들을 감지하거나 해결하지 않습니다.

 

메모리 누출
만약 프로그램이 더 이상 사용하지 않는 객체를 반환하는데 실패하면 이 문제가 발생합니다. 다음의 코드는flag가 false 값을 갖고test함수가 호출될 때 마다 100 바이트의 메모리를 낭비합니다.

 

void read_file(char*);

void test(bool flag)

{

    char* buf = new char[100];

    if (flag)

    {

        read_file(buf);

        delete [] buf;

    }

}

 

프로그램이 계속 메모리를 누출시킨다면 성능은 떨어지게 됩니다. 그리고 런타임 메모리의 양은 계속 증가하게 되고, 스와핑하는데 더 많은 시간을 소비하다가 결국에는 메모리를 전부 사용하고 말게 됩니다.

 

너무 이른 메모리 해제
이것은 아직 사용중인 객체를 반환시킬때 발생하여 데이터를 변형시키거나 코어덤프를 포함한 간혈적인 실패를 야기할 수 있습니다. 메모리가 반환되면, 그것은 시스템의 heap의 일부분이 되며 후에 다른 프로그램의 일부분을 위해 재할당 될 수 있습니다. 만약 이미 반환된 메모리에 접근하려고 시도한다면 프로그램과 전혀 무관한 다른 데이터를 접하게 될 수도 있습니다. 메모리의 변경은 한번에 갑자기 나타날 수 있으며, 실제 에러가 발생한 부분과 동떨어진 부분에서 나타나 디버깅을 매우 힘들게 할 수도 있습니다.

다음 코드는 문제를 보여주고 있습니다.

 

void eval(int*);

void test2()

{

    int* p1 = new int;

    int* p2 = p1;

    *p1 = 1;

    eval(p1);

    delete p1;

    *p2 = 2; // p2가 가르키는 메모리는 이미 삭제되었습니다.

    eval(p2);

}

 

이 경우의 차이는 같은 메모리 포인터가 한번 이상 삭제된다는 것입니다.

 

void test3()

{

    int* p1 = new int;

    int* p2 = p1;

    delete p1;

    delete p2; // 또 삭제합니다!

 


 

메모리 단편화
이것은 빈번한 메모리의 할당과 반환으로 일어나며 프로그램의 성능을 저하시킬 수 있습니다. 메모리 단편화는 큰 메모리 덩어리가 작게 쪼개져 조각으로 흩어질 때 발생합니다. 이렇게 작고 인접해 있지 않은 메모리 덩어리들은 프로그램에서 많이 사용되지 않으며 다시 할당되지 않을 수도 있습니다. 이런 과정으로 프로그램은 실제 할당된 메모리보다 더 많은 메모리를 사용하게 됩니다.

 

메모리 덮어쓰기
이것은 너무 작은 메모리가 객체에 할당될 때에 발생합니다. 이것의 결과는 메모리의 변형이나 간혈적인 실패들을 포함할 수 있습니다. 프로그램은 때로는 올바르게 작동하고 때로는 이상하게도 작동하지 않습니다. 이 문제를 보여주는 예제입니다.

 

void test4()

{

    char* x = new char[10];

    x[10] = '\0'; // memory overwrite!

}

libgc라이브러리는 이런 종류의 메모리 문제를 감지하거나 해결하지 않습니다. 이런 에러를 찾기 위해 디버거에 Run Time Checking(RTC) 기능을 사용할 수 있습니다.

 
libgcAPI
사용자 삽입 이미지

Thelibgc라이브러리는 C의 메모리 관리 함수들인malloc(),calloc(),realloc(),free()를 포함하고 있습니다. 또한 사용자의 프로그램에서 호출해 사용할 수 있는 다음의 3개의 함수를 제공합니다. 사용자의 프로그램에서 이 함수들을 사용하기 위해서는 헤더에<gct.h>을 포함하고 있어야 합니다. 라이브러리의 개발 모드 이하의 모든 함수를 사용할 수 있습니다.libgc모드의 설명 부분을 보십시오. 

void gcFixPrematureFrees(void)
너무 이른 메모리 해제 문제를 해결합니다. 이 함수가 호출된 후엔free()함수는 아무것도 하지 않습니다.free()로 전달된 포인터는 해제되지 않습니다. 포인터는 더 이상 사용되지 않게 될때 반납됩니다.

Note-
너무 이른 메모리 해제 문제를 해결하는 과정에 있어, 라이브러리가 메모리가 완전히 사용되지 않는다는 것이 확인될 때까지 메모리를 해제를 미루기 때문에 어떤 프로그램에서는 메모리의 사용률을 높일 수도 있습니다. 이것은 대부분은 프로그램에서 중요한 사항은 아니지만 메모리 제한이 엄격한 프로그램에서는 중요할 수도 있습니다.
 
 

void gcStopFixingPrematureFrees(void)
이른 메모리 해제 문제를 해결하는 것을 멈춥니다. gcFixPrematureFrees 의 호출과 반대의 효과를 가져옵니다.

 

void gcInitialize(void)
이 함수는 사용자에게 사용자 자신의 셋팅을 설정 할 수 있도록 해주고 보통의 위에서 기술된 다른 API와는 조금 다르게 동작합니다. 차이점은libgc라이브러리가gcInitialize을 초기에 호출 하므로 사용자의 프로그램에gcInitialize를 직접적으로 호출 하면 안됩니다. 사용자는 다음의 예와 같은 방법으로 프로그램에 사용자 고유의gcInitialize함수를 정의할 수 있습니다.

 

#include <gct.h>

. . .

void gcInitialize(void)

{

    gcFixPrematureFrees();

}

 
작동 모드들
사용자 삽입 이미지

라이브러리는 두가지의 다른 작동 모드를 가지고 있습니다. 각각의 모드에서 링크시에CC -library=gc-lgc옵션으로libgc라이브러리를 사용할 수 있습니다.-library=gc옵션은 C++ 컴파일러에서만 실행 가능합니다. 

배치 모드(Deployment Mode)
이미 존재하는 코드를 수정하기를 원치 않거나 코드에 접근할 수 없어서 수정할 수 없을 때 이 모드를 사용할 수 있습니다. 배치 모드에서libgc라이브러리를 사용하기 위해서는 사용자의 프로그램과 라이브러리를 링크하면, 자동적으로 메모리 누수와 메모리 단편화 현상이 해결됩니다. 또한 라이브러리 자체의 효율적인malloc()함수를 사용하기 때문에 메모리 할당 속도도 빨라집니다.

 

개발 모드(Development Mode)
개발 모드는 배치 모드의 모든 기능을 동일하게 제공합니다. 거기에 추가하여deletefree()의 호출 없이도 프로그램을 작성할 수 있습니다. 라이브러리는 사용되지 않는 메모리를 반납합니다. 이러한 기능은 자바 프로그래밍의 장점으로 C 와 C++ 프로그래머에게 제공됩니다. 만약deletefree()를 프로그램에 사용한다면,gcFixPrematureFrees()gcStopFixingPrematureFrees()를 호출함으로써 코드의 특정한 영역에서의 너무 이른 메모리 해제를 막을 수 있습니다.

 
요약
사용자 삽입 이미지

사용자의 프로그램에서libgc라이브러리를 사용하는 것은 다음과 같은 장점이 있습니다.

  • 프로그램의 메모리 누수를 방지할 수 있습니다. 심지어 프로그램이 메모리를 낭비하는 다른 라이브러리를 사용한다고 하더라도libgc와 연결되어 자동으로 메모리 누수를 해결해 줍니다.
  • deletefree()를 호출하지 않고도 프로그램을 작성할 수 있습니다. 이것은 자바 프로그래밍 환경의 장점이지만 C와 C++ 프로그램에서도 이용할 수 있습니다.
  • 코드내에서 너무 이른 메모리 해제를 방지합니다.
  • 빠르며 단편화 되지 않는 메모리 할당기를 제공합니다.libgc라이브리와 연결 된 프로그램은 실행속도가 빠르고, 더 작은 공간을 사용하며, 메모리 단편화가 일어나지 않습니다.


저자에 관하여
사용자 삽입 이미지

Mukesh Kapoor는 C++ 컴파일러 그룹의 수석 엔지니어 입니다. 그는 인도 공대와 델리 대학에서 각각 MS, BS 학위를 가지고 있습니다. 썬에 입사 하기 전에 그는 Peritus, Inc와 Plexus 컴퓨터, Elxsi, Unisoft에서 근무한 경험이 있습니다.