2014년 1월 26일 일요일

C# 메모리 관리 기법 - Boxing

가비지로 인한 부하도 크지만 박싱으로 인한 부하도 무시할 수 없습니다. 

박싱이란 것은 Value type 객체를 Reference type객체로 바꾸는 것을 말하는데요.

C#에서는 모든 객체가 object로부터 상속됩니다. 
int, float같은 Value type조차도 object로부터 상속받은 것처럼 사용할 수 있습니다.

이런것들을 주의하지 않고 코드를 짜게 될경우 의도치 않게 박싱이 일어나는 곳들이 생기게 되고 이것이 누적되면 무시할 수 없는 속도저하가 일어나게 됩니다. 

아래의 예를 보시면, 리스트에 서로 다른 여러 종류의 type 값을 추가하는데 foreach문 안에서 item을 object로만 받아서 처리하고 있는것을 보실 수 있습니다. 
class MyClass
{
    public override string ToString() 
    { 
        return "다섯"; 
    }

    static public void Sample()
    {
        ArrayList list = new ArrayList();

        // 리스트에 아래처럼 여러가지 타입의 값을 추가해 넣어줍니다.
        // int형 float형 string형 등등.....
        list.Add(1);
        list.Add(1.5f);
        list.Add(‘3’);
        list.Add("four");
        list.Add(new MyClass());

        // 리스트에서 값을 꺼낼때 오직 object로 받아서 처리하고 있습니다.
        // 이런 경우 Value type을 Reference type으로 바꿔주면서 시간이 오래 걸리고
        // 변환하면서 heap에 저장이 됩니다. 
        // 시간도 오래 걸리고 가비지도 생기게 되겠네요.
        foreach (object item in list)
        {
            Debug.log(item.ToString());
        }
    }
}
그래서 여러 type을 처리해야만 하는 경우가 아니라면 값의 type을 명시할 수 있는 Generic collection의 사용이 좋겠습니다. 
Generic은 C++의 template과 비슷해서 C++의 STL container와 비슷하게 생겼습니다. 

아래의 예제에서 잘 사용한 경우와 그렇지 않은 경우를 비교해서 보세요.
class Example
{
    // 일단은 여러가지 type을 사용하지 않도록 분리해서 작업하는것이 제일 좋습니다. 
    // 분리가 끝나면 적절한 type을 명시할 수 있는 Generic collection을 사용하구요.

    // 좋지 못한 경우
    static public void BadCase()
    {
        // type에 대한 명시가 없는 어레이 리스트를 사용합니다.
        ArrayList list = new ArrayList();

        // 값은 int형인데
        int evenSum = 0;
        int oddSum  = 0;

        for (int i = 0; i < 1000000; i++)
        {
            list.Add(i);
        }

        // item을 object로 받아서 처리하게 되네요
        // 박싱이 일어납니다.
        foreach (object item in list)
        {
            if (item is int)
            {
                int num = (int)item;

                if(num % 2 ==0)
                {
                    evenSum += num;
                }
                else 
                {
                    oddSum += num;
                }
            }
        }
           
        Console.WriteLine("EvenSum={0}, OddSum={1}", evenSum, oddSum);
    }

    // 적절하게 잘 사용한 경우
    static public void GoodCase()
    {
        // type을 명시해줬습니다.
        List<int> list = new List<int>();

        // int형을 사용했구요
        int evenSum = 0;
        int oddSum  = 0;

        for (int i = 0; i < 1000000; i++)
        {
            list.Add(i);
        }

        // num을 int형으로 받아서 처리합니다. 
        // 박싱이 일어나지 않습니다.
        foreach (int num in list)
        {
            if (num % 2 == 0)
            {
                evenSum += num;
            }
            else
            {
                oddSum += num;
            }
        }
           
        Console.WriteLine("EvenSum={0}, OddSum={1}", evenSum, oddSum);
    }
}





댓글 없음:

댓글 쓰기