ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 13주차 5일 기술 면접 대비 ( 가비지 컬렉터, 박싱 언박싱 주의점 )
    개발일지/스파르타 코딩클럽 부트캠프 2024. 7. 16. 09:01

     

    using System;
    
    public class Logger
    {
        public string LogMessages { get; private set; }
    
        public Logger()
        {
            LogMessages = string.Empty;
        }
    
        public void Log(string message)
        {
            LogMessages += message + "\n";
        }
    }
    
    public class Program
    {
        public static void Main()
        {
            Logger logger = new Logger();
            
            for (int i = 0; i < 10000; i++)
            {
                logger.Log("This is log message number " + i);
            }
    
            Console.WriteLine("Logging completed. Total log length: " + logger.LogMessages.Length);
        }
    }

     

    1. 위의 코드가 문제가 되는 이유를 메모리 관점에서 설명해주세요.

     

    string 객체는 값이 변경되면 메모리에서 이전 참조 값을 버리고 새로운 참조 값으로 변경되기 때문에 문자열을 반복적으로 수정해야하는 경우 메모리적으로 손해가 많다. 버려진 참조 값은 가비지 컬렉터로 들어간다.

     

    2. 아래와 같이 string이 아닌 StringBuilder가 권장되는 이유는 무엇일까요?

     

    public class Logger
    {
        private StringBuilder logMessages;
    
        public Logger()
        {
            logMessages = new StringBuilder();
        }
    
        public void Log(string message)
        {
            logMessages.Append(message).Append("\n");
        }
    
        public override string ToString()
        {
            return logMessages.ToString();
        }
    }

     

    StringBuilder 객체는 참조 값이 변경되지 않고 이전 값을 버리는 구조가 아니기 때문에 힙 메모리에서 값이 삽입, 추가 , 제거 된다. 간단하게 설명하면

    String = 값이 추가되면 계속해서 새로운 값을 생성해서 붙여넣기, 변경되면 이전 값을 가비지컬렉터로 가고 새로운 값을 생성

    StringBuilder = 값이 추가되면 기존 값에 추가되는 값을 추가, 변경되면 이전 값이 힙 메모리에서 제거

    이미 만들어진 틀 안에서 작동한다고 보면 좋을 거 같다.

     

    설명 문제

     💡 질문을 직접 설명해보고, 모르는 부분이 있다면 알아보는 시간을 가집시다.

     

    1. 가비지 컬렉터란 무엇인가요?
    2. 가비지 컬렉터의 장점과 단점에 대해 설명해주세요.
    3. 가비지 컬렉터의 세대 개념에 대해 설명해주세요.
    4. 박싱, 언박싱을 사용할 때 주의해야 할 점은 무엇일까요?
    5. 오브젝트 풀을 사용하면 메모리 관리에 도움이 되는 이유가 무엇일까요?

     

    1번 답 : 가비지 컬렉터는 말 그대로 쓰레기 수집이다. 메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. C#에서는 CLR이 자동적으로 메모리 관리를 해준다. 그 중 하나가 가비지 컬렉션이며, 이 가비지 컬렉션을 담당하는 역할이 가비지 컬렉터이다.

     

    2번 답 : 

    가비지 컬렉터의 장점 : 해제된 메모리의 접근을 방지해준다, 해제된 메모리를 다시 해제하는 것을 방지해준다, 메모리 누수가 안된다, 메모리 관리를 자동으로 해주기 때문에 개발 생산성이 향상된다, 코드가 간결해진다 ( 메모리 관리 코드를 따로 안짜도 돼서 )

    가비지 컬렉터의 단점 : 가비지 컬렉션이 일어나는 시점이나 점유 시간의 예측이 힘들다, 가비지 컬렉션 실행으로 인한 오버헤드가 발생한다. 주기적으로 실행되기 때문에 일어나는 단점이다. 또, 불필요한 메모리를 사용한다. 자동으로 메모리를 관리해주는 것은 물론 이점이지만 그것은 원하는 시점에 정확한 시점의 해제가 어렵다는 뜻이라서 때때로 불필요한 메모리 공간을 점유하는 상황도 발생한다.

     

    3번 답 : 

    가비지 컬렉터의 세대는 몇 번의 가비지 컬렉션을 거쳤는지를 말한다. 가비지 컬렉션이 일어나고 stack 메모리에서 아직 참조되고 있는 object 들에 대해서는 세대 수를 증가시킨다. 처리 순서를 알아보자면 먼저 새로 힙 메모리에 할당되는 object의 경우 0세대에 저장한다. 후에 0세대가 꽉차게되면 가비지 컬렉션이 일어나고 사용 되고 있는 object들은 1세대에 저장된다. 동일하게 1세대에 대해서 가비지 컬렉션이 일어나면 살아남은 object들은 2세대에 저장된다. 2세대까지 옮겨진 object들은 더 이상 다른 곳으로 옮겨가지 않는다. 만약 2세대까지 꽉 차서 가비지 컬렉션이 일어나면 가비지 컬렉터는 모든 세대에 대해서 가비지 컬렉션을 진행한다. 이것을 Full GC라고 부르며 이 때 일정 시간 프로그램을 멈추고 가비지 컬렉션을 하기 때문에 가비지를 잘 관리할 필요가 있다.

     

    4번 답 : 

    박싱과 언박싱은 사실 최적화 관점에서는 지양해야하는 기법이다. 내부적으로 상당한 오버헤드를 감수하고 사용해야하기 때문인데, 박싱은 1. 힙 영역에 새로운 메모리를 할당하고 2. 스택의 값을 힙 메모리로 복사 3. 힙 메모리의 주소 값을 갖는 새로운 스택 메모리를 할당하는 과정을 거친다. 이 과정에서 박싱은 값 하나를 옮기는데 메모리 참조를 엄청 많이 한다는 것을 알 수 있다. 이로 인해 시간적 오버헤드가 발생한다.

    언박싱도 비슷한 이유이다. 하지만 언박싱은 여기에 가비지를 생성한다는 문제를 더 가지고 있다. 즉 언박싱은 그 자체로도 오버헤드가 발생하지만 가비지를 생성함으로써 GC를 동작시키는 잠재적 오버헤드까지 가진 셈.

     

    5번 답 : 

    오브젝트 풀링은 최적화 기법 중 하나로 오브젝트 풀(웅덩이) 를 만들어놓고 필요할 때마다 객체를 꺼내서 쓰는 것을 말한다. 예를 들어서 총알, 뱀서류 게임의 몬스터 등 엄청나게 많은 수의 오브젝트를 계속해서 생성하고 파괴한다면 계속해서 메모리를 할당하고 해제하면서 엄청난 수의 가비지 컬렉터가 발생한다. 하지만 오브젝트 풀링 기법을 사용하면 계속해서 생성, 파괴 하는 것이 아닌 처음부터 총알 30개를 만들어 놓고, 거기서 꺼내와서 총알을 발사 후 , 총알이 사라질때는 다시 넣어주는 방법으로써 생성과 파괴가 아닌 꺼내서 쓰는 느낌으로 가비지 컬렉터를 발생시키지 않는다. 사실 오브젝트 수가 많은 구현을 하려면 오브젝트 풀링은 필수 기법이라고 할 수 있다.

Designed by Tistory.