2014년 1월 23일 목요일

C# 메모리 관리 기법 - string

유니티는 다양한 언어를 지원해서 개발을 좀더 수월하게 해주는데 저는 주로 Java보다 C#을 애용합니다. 

Java가 주는 간결하고 자유로움이 매력적이긴 하지만 개발이 진행될수록 그 자유스러움이 오히려 독이 되는 경우가 많다고 느껴지기 때문인데요. 

C#을 다루면서 참 코딩이 쉽다라고 느낀적이 참 많을 정도로 좋은 언어인 것은 틀림없습니다.

C#의 장점이라 할수 있는 특징들을 보면

  • 메모리 해제에 신경 쓰지 않아도 됩니다.
  • 이미 삭제된 메모리에 접근하는 실수를 방지해줍니다.
  • 잘못된 캐스팅으로 엉뚱한 메모리에 접근하지 않게 합니다.
  • 배열 크기보다 큰 메모리에 접근하지 못합니다.
  • 메모리 단편화에 대해 신경 쓰지 않아도 됩니다.
등등이 있겠네요. 
이런 장점들때문에 메모리를 무시하고 막 만들다 보면 가비지 컬렉션때문에 랙이 발생하는 경우가 다들 한번씩은 있을거에요.

어쨌든 C#을 사용하면서 알아두면 좋을 메모리 관리 기법에 대해서 써볼께요.
앞으로 하드웨어가 더 발전하면 메모리 관리도 필요 없어지지 않을까 싶네요. ^^;;

주기적으로 랙이 막 생기는데 로직의 이상도 없고 다른 부분에서는 특정 이상이 발견되지 않는다. 


그런데!! 

프로파일러를 돌려보니 System.GC.Collect()에서 호출 시간이 오래걸린다!!!

이거 십중팔구 가비지 컬렉션 문제입니다.


그럼 가비지 컬렉션이 작동하는 횟수를 줄여서 랙 발생 횟수를 줄이면 되겠죠. 

그러려면 가비지가 최대한 발생하지 않도록 해주어야 합니다.

그렇게 하기 위한 방법들중 string에 대해서 살펴보겠습니다.


C#은 문자열 조합하는 방법이 너무 쉽습니다. 


그래서 아래처럼 무턱대고 '+' 연산자를 이용해서 문자열을 만드는 경우가 많은데 이렇게 하면 '+'할때마다 새로운 string 인스턴스가 생성됩니다. 


그리고 이게 곧 가비지가 됩니다.


class Names
{
    public string[] name   = new string[100];

    public void Print()  
    {
        for (int index = 0; index < name.Length; index++)
        {
            // 아래처럼 '+'를 마구 쓰면 이게 다 가비지가 됩니다.
            string output  = "[" + index + "]" + name;
            Console.WriteLine(output);
        }
    }
}
이럴때 아래처럼 System.Text.StringBuilder를 사용하면 이런 문제를 해결 할 수 있습니다.

class NewNames
{
    public string[] name              = new string[100];
    private StringBuilder sb          = new StringBuilder();

    public void Print()
    {
        sb.Clear(); 

        for (int index = 0; index < name.Length; index++)
        {
            sb.Append("[");
            sb.Append(index);
            sb.Append("] ");
            sb.Append(name);
            sb.AppendLine();
        }
        
        Console.WriteLine(sb.ToString());
    }
}
이미 잡아 놓은 메모리 공간에 문자열을 복사해서 한번에 ToString()으로 string 객체를 생성해내기 때문에 가비지가 발생하지 않습니다.

Append()가 너무 많아 코드가 드럽다고 느껴지면 AppendFormat()을 사용할 수 도 있습니다.


class NewNamesOther
{
    public string[] name              = new string[100];
    private StringBuilder sb          = new StringBuilder();

    public void Print()
    {
        sb.Clear(); 

        for (int index = 0; index < name.Length; index++)
        {
            sb.AppendFormat("[{0}] {1}", index, name.ToString());
        }
        
        Console.WriteLine(sb.ToString());
    }
}
string처럼 Immutable pattern을 사용한 객체들의 값은 기존 메모리를 수정하지 않고 새로운 메모리를 만들어 반환하므로 주의해야합니다.

댓글 없음:

댓글 쓰기