String vs StringBuilder

2023. 10. 13. 15:54C#

 

불변 객체 String

 

 Value타입이 아닌 Ref타입입니다. 즉, 문자열을 조합할때마다 새로운 Class가 생성되며 이전의 데이터는 GC(가비지 콜렉터)에 의해 수집된다. 따라서 문자열을 조합할때마다 부하가 발생하게 된다.

 

이 경우 총 3개의 string 인스턴스가 생성되고 조합 과정에서 앞서 2개의 인스턴스는 GC에 의해 언젠가 수집될 것이다. 따라서 메모리 낭비가 발생하며 비효율적인 코드가 생성되는 것이다.

 

String은 왜 불변 객체가 되었을까?

 

(https://www.c-sharpcorner.com/UploadFile/230635/why-string-are-immutable-in-dotnet/) 외국 String Immutable 이유 번역

 
"불변 문자열"의 의미
 
불변(immutable)의 사전적 의미는 '시간이 지나도 변하지 않거나 변경될 수 없는'입니다. 이는 String 개체에 값이 할당되면 변경할 수 없음을 의미합니다. 예, 올바르게 읽으셨습니다. 다음 코드를 고려해보세요.
 
 
다음은 이 코드의 출력입니다.
 
abcdef
abcdefghijkl
 
myString의 값을 "abc"에서 "abcdef"로 변경한 다음 "abcdefghijkl"로 변경한 것처럼 보이지만 실제로는 그렇지 않았습니다! 그것을 이해하려고 노력합시다. 첫 번째 단계에서는 "abc" 값을 가진 새 문자열 개체가 힙에 할당되고 myString은 이 메모리 위치를 가리킵니다. 2단계(myString += "def";)에서는 "abcdef" 값으로 힙에 새 문자열 개체가 할당되고 myString은 이제 이 새 메모리 위치를 가리킵니다. 그러나 "abc"라는 문자열은 여전히 ​​힙에 존재합니다. 따라서 우리는 그 중 하나만 참조하고 있음에도 불구하고 실제로 힙에 두 개의 문자열 개체를 가지고 있습니다. 같은 방식으로 계속하면 이 코드의 끝에는 4개의 문자열 개체가 있습니다. 단 하나의 개체만 참조되고 나머지 세 개는 사용되지 않습니다. 이전 코드의 다음 메모리 할당 다이어그램을 보면 상황이 더 명확해집니다.
 
이제 우리는 그 이유에 대한 맥락으로 넘어갈 것입니다.
 
 
.Net에서 문자열을 변경할 수 없는 이유
 
첫번째로 .Net 디자이너는 불변 텍스트 문자열을 구현하기로 결정했습니다. 이 아키텍처에는 여러 가지 이유가 있습니다. 프로그래머가 동일한 값을 가진 여러 문자열 변수를 가지고 있는 경우 동일한 문자열 값에 대해 메모리를 여러 번 할당하는 것을 피할 수 있습니다. 문자열에 메모리를 한 번 할당하면 모든 변수가 동일한 메모리 블록을 가리킵니다. 다음 코드 블록을 고려하십시오.
 
이 코드의 메모리 할당은 다음과 같습니다.

 

 
문자열이 변경 가능한 경우 str1 값을 변경하면 str2 및 str3 값도 변경되지만 이는 바람직하지 않습니다.
둘째, .Net 에서 string이 변경 불가능 객체인 이유는 멀티스레드 환경을 고려한게 커 보입니다.여러 프로세스가 엑세스를 하게 되면 경쟁 상태가 될 수 있고 이로 인해서 lock을 걸어야 하는 등의 부가적인 사항들이 들어가게 됩니다.대신 변경이 안되므로 이런 것들을 고려할 필요가 없게 되는 것이죠.
셋째, 문자열 불변성의 또 다른 이유는 문자열을 해시테이블의 키로 잘 활용하기 때문입니다. 해시 값이 계산되는 객체는 해시 값이 시간에 따라 일정할 수 있도록 변경할 수 없어야 합니다.
문자열 불변성의 또 다른 멋진 점은 System.String이 클래스임에도 불구하고 문자열 개체가 값 유형과 같은 동등성과 비교된다는 것입니다. 이는 불변 객체의 정체성이 객체의 상태라고 생각할 수 있기 때문에 가능합니다. 다음 코드를 고려해보세요.
 

 

 
str1과 str2가 서로 다른 두 개체를 참조하더라도 앞의 코드는 true를 반환합니다.
 
 
가변 객체 StringBuilder

 

Stringbuilder를 사용하면 문자열을 조합할때마다 새로운 변수를 생성할 필요가 없다. Stringbuilder 내부 자체적으로 함수를 가지고 있어서 값들을 조합하고 삭제할때 인스턴스를 추가발생시키지 않는다.

 

StringBuilder는 가비지가 정말 없는 것인가?

 

일단 string을 변경하면 새 객체가 생성되어 이전 객체는 가비지가 된다. 그럼 stringbuilder는 어떤 식이라 가비지 생성이 없는 걸까?

 

일단 stringbuilder는 16문자 크기의 자리를 잡게 된다. 일단 기본적으로 제공되는 16크기의 문자를 만들고 이를 꽉 채웠다고 하면 이동안 stringbuilder에서 가비지를 생성하지 않는다.

 

이제 꽉 채운 상태에서 append를 하게 되면 더 큰 용량의 버퍼가 잡히게 되고 원래 버퍼의 데이터가 새 버퍼에 복사되는 과정이 필요하게 된다. 이 과정에서 이전의 버퍼는 GC의 수거대상이 되는 것이다.

 

여기까지가 닷넷 4.0까지의 이야기라고 한다. 지금은 새 버퍼를 할당한 뒤 링크를 구성한다고 합니다때문에 복사, 이전 버퍼가 가비지가 되는 등의 일은 없어져 성능이 이전보다 좋아졌다는 것입니다.

출처: https://husk321.tistory.com/357 [껍데기방:티스토리]

 

결론

 

이와같은 String과 StringBuilder를 그러면 언제 사용해야 하는지가 가장 중요할 것이다.

 

문자열의 추가, 수정, 삭제에 있어 빈번하게 발생할 경우 string보다 stringbuilder가 더 효율적일 것이다.

 

String

 

- 문자열 수정이 적고 반복적인 작업이 아닌 경우 (성능 향상)

- 문자열의 수가 적을 경우 (stringbuilder는 string에 비해 무시해도 좋을 정도의 성능향상을 보이거나 전혀 없을수가 있다)

- 문자열을 작성하는 동안 광범위한 검색 작업이 예상될때 (왜냐하면 stringbuilder에는 IndexOf 또는 StartWith같은 함수가 없기 때문이다)

 

StringBuilder

 

-  문자열 수정이 빈번하고, 반복적인 작업일 경우

-  많은 수의 문자열 조합 또는 결합이 필요한 경우

-   응용 프로그램이 설계시에 알 수 없는 횟수의 문자열을 변경해야 할 때 (사용자의 입력등으로 문자열 조합이 이루어질 때)

'C#' 카테고리의 다른 글

프로퍼티 (Property)  (0) 2023.10.10
C# 작동 방식 .Net Framework, CLR  (0) 2023.10.09
C# LINQ(Language-Integrated Query)  (0) 2023.10.08
C# 콜백, 델리게이트, 이벤트  (0) 2023.10.07