Mutable(가변객체) immutable(불변객체) 에 대한 질문

안녕하세요 C# 개발자 코드몽키입니다

제목처럼 mutable 과 immutable에 대한 질문입니다.

그전에 먼저 mutable과 immutable에 대한 정의를 해보자면
mutable: 객체 생성 후 해당 메모리의 값이 변할수 있는 객체
immutable: 객체 생성 후 해당 메모리의 값이 변경되지 않는 객체

쉽게 생각해서 객체 생성 후 값을 변경했을 때 동일한 메모리에 데이터값을 변경시키느냐
새로운 메모리에 값을 이동시키느냐 라고 이해를 했었습니다.

해당 내용을 확인하기 위하여 vs에서 메모리값을 확인하는 예제를 만들었습니다.

1. int가 가변인지 불변인지 확인하기 위한 코드

int i = 10;
int* ptri1 = &i;

i = 20;
int* ptri2 = &i;

이름 형식
:arrow_forward: ptri1 0x00000059dc39ea6c int*
:arrow_forward: ptri2 0x00000059dc39ea6c int*

동일한 메모리에서 값 변화가 일어남
RESULT: 가변

2. List가 가변인지 불변인지 확인하기 위한 코드

List list = new List();
list.Add(0);
int list1 = list[0];
int* ptrList1 = &list1;

list[0] = 5;
int list2 = list[0];
int* ptrList2 = &list2;

이름 형식
:arrow_forward: ptrList1 0x00000059dc39ea4c int*
:arrow_forward: ptrList2 0x00000059dc39ea3c int*

메모리 이동과 함께 값 변화가 일어남
RESULT: 불변

3. string, StringBuilder가 가변인지 불변인지 확인하기 위한 코드

string str = “test”;
char str1 = str[0];
char* ptrStr1 = &str1;

str += “test”;
char str2 = str[0];
char* ptrStr2 = &str2;

//////////////////////////////////////////

StringBuilder sb = new StringBuilder();
sb.Append(“test”);
char sb1 = sb[0];
char* ptrSb1 = &sb1;

sb.Append(“test”);
char sb2 = sb[0];
char* ptrSb2 = &sb2;

이름 형식
:arrow_forward: ptrStr1 0x00000059dc39ea24 char*
:arrow_forward: ptrStr2 0x00000059dc39ea14 char*
:arrow_forward: ptrSb1 0x00000059dc39e9fc char*
:arrow_forward: ptrSb2 0x00000059dc39e9ec char*

메모리 이동과 함께 값 변화가 일어남
RESULT: 불변

최종결론
int는 동일한 메모리에서 값 변화가 나타나고
list, string, stringbuilder에서는 값 변경을 할 경우 메모리 위치가 변경됩니다.

질문을 올린 이유는
인터넷 블로그의 글을 보면
string - immutable
StringBuilder - mutable
이기 때문입니다.

그런데 제가 테스트 했을 때는 string과 StringBiulder가 동일하게 immutable로 보이기 때문에 질문드립니다.
제가 지금 예제를 잘못 만든 것인지 개념을 잘못 이해하고 있는 것인지 답변 부탁드리겠습니다.

위 내용 보시고 혹시 이해가 안되거나 이상한부분있으면 말씀해주십시요.

감사합니다!

좋아요 2

가변과 불변은 메모리가 변하느냐 변하지 않느냐가 아니라 값이 설정된 후 변경할 수 있느냐 없느냐를 의미합니다.

string은 설정되면 수정할 수 없으므로 불변입니다.
StringBuilder는 값이 설정되더라도 변경할 수 있으므로 가변 입니다.

좋아요 5

안녕하세요 코드몽키님.
우선 예제가 잘 이해가 가지 않는 부분이 있습니다.

1번 테스트는 같은 스택공간의 주소를 넘기셨으니 같은 주소값이 나올 수 밖에 없고,
2번 테스트 역시 0번 인덱스 공간의 주소를 넘기셨으니 같은 주소값이 나올 수 밖에 없습니다. (사실 List list = new List() 문법 때문에 C#으로 테스트하신게 맞는지도 모르겠습니다.)
3번 테스트 역시 string 에서 str += "test"; 코드를 통해 주소값을 바꿔주셨으므로 주소값이 같게 나옵니다. stringbuilder도 첫번째 인덱스만 참조하시니 당연히 같게나옵니다.

명확하게 어떤 것을 테스트하셨는지 잘 모르겠습니다.

우선 .NET에는 Intern Pool이라는 것이있어, 리터럴 문자열에 대한 GC가 관리하지 못하는 특수한 Heap 이 존재합니다.

3번 테스트의 코드를 해석하자면,

string str = "test"; 에서 "test"는 쌍따옴표로 쌓여진 리터럴 문자열이므로, 컴파일 단계에서 Intern Pool로 따로 할당이 됩니다. 따라서 string str의 str에는 Intern Pool의 Heap 주소를 가리키게 됩니다. 이후
str += "test"; 를 하게되면 기존의 “test” 문자열에 대한 변형이 생겨서 "testteset"라는 새로운 문자열이 생성되고 이것은 런타임에서 생성된 문자열이므로 리터럴 문자열이 아니라서 GC Heap의 어딘가에 새로운 문자열이 생성되고, 그 주소값을 str이 저장하게 됩니다. .NET 에서 string이 불변객체라는 것은 이런 뜻입니다.

string을 코드로 변형시키는 시도를 하면, 문자열 자체가 변형되는 것이 아니라 "test"가 있고, "testtest"가 다른 메모리에 새로운 문자열로 할당되는 것입니다. 따라서 기존의 "test"가 변형된게 아니므로 string은 .NET 에서 불변객체입니다.

3번의 stringbuilder는 내부적으로 unsafe와 객체들의 연결리스트 형태로 설계되어있습니다. stringbuilder는 문자열에 변형을 가할 수 있어, 이미 할당된 문자열을 수정할 수 있습니다. unsafe가 이것을 가능하게 합니다. 또한 Append 메서드를 사용할 때 마다, 새로운 string이 할당되고 각각의 string 객체가 따로 놀고있다가 마지막에 ToString() 메서드를 사용할 때 하나로 합쳐줍니다.
따라서 3번 테스트는 …테스트를 잘못하신 것 같습니다.

어떤 테스트를 시도하셨는지 명확하지 않지만, .NET에서의 string 불변은 이해하실 것 같습니다.

좋아요 3

P.S StringBuilder 의 동작 원리를 자세하고 명료하게 설명한 Andrew Lock 님 블로그 글을 뜬금없이 댓글 달아봅니다.

좋아요 4

우선 궁금한 건…

인터넷 블로그의 글을 보면
string - mutable
StringBuilder - immutable
이기 때문입니다.

누가 이런 잘못되 정보를…=ㅂ=;;;;

그리고 전체적인 설명은 @dimohy , @Vincent 님께서 정확히 해주셨습니다. >ㅁ<b

여기에 하나만 더 사족을 붙이자면
immutable 은 이미 할당된 객체 내부의 값을 변경할 수 있느냐가 관건입니다.
이미 할당한 변수에 새로운 값을 할당할 수 있느냐가 중요한 게 아니에요.

헤헷 ~ㅂ~*

좋아요 3

인터넷 블로그의 글을 보면
string - mutable
StringBuilder - immutable
이기 때문입니다.

제가 반대로 넣었었네요;;;
다시 수정해놓도록 하겠습니다ㅠㅠ

좋아요 1

친절하고 자세한 답변 감사합니다.
내용을 자세히 읽어보니 제가 오해하고 있던 부분이 있었던듯 합니다.

List<int> list = new List<int>();

인데 제가 코드로 표시를 안하니 꺽쇠괄호가 안나타났네요…

다시한번 답변 감사합니다

좋아요 1

뮤터블과 임뮤터블의 단어가 최초에 어디에서 붙어 사용된건지는 모르겠으나, 사실상 요즘에는 그냥 그 특정 상황에서 불변이냐 가변이냐를 이야기할때 사용될 수 있는 단어라고 생각합니다.

예컨데, 메모리 기준으로 불변이냐 가변이냐를 이야기할때 쓸수도 있고

개체 디자인할때 불변 개체냐 가변 개체냐를 이야기할 때 쓸수도 있습니다.

심지어 도커의 장점중 하나인 불변 환경에서도 임뮤터블이라는 단어를 사용합니다.

그래서 제 개인적인 생각으로는 어느 상황에서 이야기하냐에 따라 그에 맞게 해석하고 받아드리는게 좋다고 생각있어요 ^^

좋아요 4