Ssssong += Dev

[C#] [Effective C#] 불필요한 객체를 만들지 말라. 본문

카테고리 없음

[C#] [Effective C#] 불필요한 객체를 만들지 말라.

ssong_dev 2022. 11. 15. 14:52

 

GC(가비지 컬렉터)는 사용하지 않는 객체들을 효율적인 방식으로 제거한다.

하지만 이러한 작업이 아무리 효율적이라 하더라도 힙에서 새로운 객체를 생성하고 삭제하는 작업은

그러한 일을 하지 않는 것에 비한다면 상대적으로 많은 프로세서 시간을 사용한다.

따라서 너무 많은 객체를 생성하면 심각한 성능 문제를 일으킬 수도 있다.

가비지 컬렉터가 과도하게 동작하지 않도록 주의해야 한다.

 

 

1. 자주 사용되는 지역변수를 멤버 변수로 변경하는 것

protected override void OnPaint(PaintEventArgs e)
{
	using (Font MyFont = new Font("Arial", 10.0f))
    {
    	e.Graphics.DrawString(DataTime.Now.ToString(), MyFont, Brushes.Black, new PointF(0,0));
    }
}

이 코드에서 OnPaint()가 호출될 때 마다 동일한 Font 객체를 매번 다시 생성한다.

GC는 이렇게 생성된 객체를 제거할 책임이 있다.

GC가 수집 작업을 수행할 지는 사용중 메모리 양과 메모리 할당 주기를 기반으로 결정된다.

따라서 메모리 할당을 자주 반복하면 사용되는 메모리 양이 많아져서 가비지 수집 작업이 수행될 가능성이 높아진다.

또한 할당 주기가 짧기 때문에 가비지 수집 작업을 더 자주 수행할 가능성이 높다.

 

따라서 이런 경우에 Font 객체를 지역변수가 아니라 멤버 변수로 변경하여 폰트 객체를 한 번만 생성한 후 이를 재사용하도록 개선할 수 있다.

 

protected override void OnPaint(PaintEventArgs e)
{
	e.Graphics.DrawString(Datatime.Now.ToString(), myFont, Brushes.Black, new PointF(0,0));
}

이처럼 코드를 수정하면 Paint 이벤트 발생 때 마다 새로운 가비지가 생성되지 않는다.

 

그런데 Font 타입과 같이 IDisposable 인터페이스를 구현한 타입의 객체를 지역변수에서 멤버 변수로 변경하면

이 클래스도 반드시 IDisposable을 구현해야 한다. 

 

이렇게 매번 생성하는 경우라면 멤버 변수로 변경하는 것이 좋으나

호출 빈도가 그리 빈번하지 않다면 굳이 변경할 필요는 없다.

 

 

 

2. 종속성 삽입을 활용하여 자주 사용되는 객체를 생성했다가 이를 재활용하는 것

 

이전 코드에서 Brushes.Black과 같은 정적 속성은

유사한 객체를 반복적으로 할당하는 것을 피하는 또 다른 기법을 보여준다.

자주 사용되는 참조 타입의 인스턴스를 정적 멤버 변수로 선언하는 방식이다.

 

private static Brush blackBrush;

public static Brush Black
{
	get
    {
    	if(blackBrush == null)
        	blackBrush = new SolidBrush(Color.Black);
           return blackBrush;
    }
}

Brushes 클래스는 이렇게 생성된 검정 브러시를 저장해두고 동일한 요청이 있을 때 마다 이 객체를 돌려준다.

결국 검정 브러시 하나를 영원히 재사용하게 된다.

 

부정적 측면으로는 경우에 따라서 생성된 객체가 메모리상에 필요 이상으로 오랫동안 남아있을 수 있다는 점이다.

또한 Dispose() 메서드를 호출해야 할 시점을 결정할 수 없기 때문에 비관리 리소스를 삭제할 수 없다는 것도 큰 단점이다.

 

 

 

3. 변경 불가능한 타입

 

변경 불가능한 타입의 대표적 예로는 System.String이 있다.

string 객체가 생성되면 이 객체가 가지고 있는 문자열의 내용은 수정이 불가능하다.

 

public static void Main(string[] args)
{
	string msg = "Hello, ";
    msg += thisUser.Name;
    msg += ". Today is ";
    msg += System.DataTime.Now.ToString();
}

이 코드에서 문자열이 수정되는 것 처럼 보이나, 사실은 완전히 새로운 string 객체가 생성되어 반환되고 있기 때문에

이전 문자 상태는 가비지가 된다.

 

string msg = string.Format("Hello, {0}. Today is thisUser.Name, DataTime.Now.ToString()");

따라서 이러한 문자열 보간을 이용하는 방법이 있다.

 

더 복잡한 경우에는 StringBuilder 클래스를 사용할 수도 있다.