[C#] string 그리고 가비지 컬렉션


유니티를 통해 3D 게임을 만들 때

최적화 문제로 굉장히 고생했던 적이 있다.

그 때 봤던 최적화 팁에서 string + string 은

가비지를 생성한다해서 StringBuilder의 Append를

이용하여 문자열을 병합하라고 했다. 그런데 도대체 왜?




해답은 바로 문자열은 변경할 수 없는 객체라는 것에 있다. (immutable)

그렇다면 변경할 수 없는 거랑 가비지 컬렉션이랑 무슨 관계가 있는 것일까?


앞서 말했듯 문자열은 한번 생성되면 변경되지 않는다.

하지만 사실 문자열을 사용하면서 변경에 대해 어려움을

느낀 경우는 잘 없을 것이다. 이러한 문자열의 표면적인

변경은 쉽지만 내부적으로는 항상 새로운 문자열이 만들어지게 된다.




string str = "하하하";

str += "쏭";


예상대로 "하하하쏭"이라는 결과가 나온다.




그러나 메모리 측면에서는 처음 생성한 str과

이후 추가 문자가 덧 붙여진 str은 완전 다른 객체이다.


즉, "쏭"이라는 글이 추가될 때 새로운 객체가 메모리에 할당되고

이 것을 str이 다시 참조하도록 되는 것이다. 처음에 만들었던

"하하하"라는 객체는 사라지게 되는 것이다. (가비지)




성능을 위해서라면 String 대신 StringBuilder를 사용하자.


후에 좀 더 자세히 다뤄볼 StringBuilder에 대해 알아보자.

앞에 말했듯이 문자열은 변경될 수 없는 객체이기 때문에

문자열에 대한 어떠한 변경도 항상 새로운 객체를 생성하게 된다.


문자열 변경 작업이 빈번하다던지, 대량으로 문자열을 연결할

경우에는 성능을 반드시 고려해볼 필요가 있다.

왜냐하면 앞서 말했듯 문자열의 변경은 가비지를 유발하기 때문.




이 대안으로 StringBuilder 클래스를 생각해 볼 수 있다.

자 그렇다면 string + string을 어떻게 하면 가비지없이

수행해낼 수 있는지 알아보도록 하자.


System.Text.StringBuilder sb = new System.Text.StringBuilder();

sb.Append("하");

sb.Append("하하");

sb.Append("쏭");


StringBuilder는 String과는 달리 변경 가능한 액체이기 때문에

(mustable) string + string의 가비지 문제를 해결할 수 있다.




추가

MSDN의 성능 고려사항을 짚고 넘어가 보자.


MSDN에 따르면 Concat 및 AppendFormat 메서드는 새 데이터를

기존 String 또는 StringBuilder 개체에 연결한다. String개체 연결

작업에서는 항상 기존 문자열과 새 데이터로 새 개체를 만든다. 


StringBuilder 개체는 연결된 새 데이터를 수용할 버퍼를 유지한다.

새 데이터는 공간이 있을 경우 버퍼 끝에 추가되고, 그렇지 않으면

더 큰 새로운 버퍼가 할당되고, 원래 버퍼의 데이터가 새 버퍼에

복사된 다음, 새 데이터가 새 버퍼에 추가된다.




String 또는 StringBuilder 개체에 대한 연결 작업의 성능은 얼마나

자주 메모리를 할당하는지에 따라 달라진다. StringBuilder 연결

작업에서는 StringBuilder 개체 버퍼가 너무 작아 새 데이터를 넣을 수 없는

경우에만 메모리가 할당되는 반면, String 연결 작업에서는 항상 메모리가 할당된다. 


따라서 고정된 수의 String 개체를 연결하는 연결 작업에는

String 클래스가 더 적합하다. 이 경우 개별 연결 작업이 컴파일러를

통해 단일 작업으로 결합될 수 있다. 임의의 개수의 문자열을 연결하는

(예 : 루푸에서 읨의의 개수의 사용자 입력 문자열을 연결할 경우)

연결 작업에는 StringBuilder 개체가 더 적합하다.





+ Recent posts