COW(Copy On Write)
Class와 Struct를 방금 공부하신 분이라면 혼동이 올 수 있으니 Class의 Memory관리(Reference Count라던지..)의 공부가 끝나신 후 보시는게 좋을 것 같습니다.
제가 공부한 내용에 틀린 부분이 있다면 지적해주세요!
Cow는 Write가 발생했을 때 Copy를 수행하는 기능으로 메모리 공간 낭비를 막기 위해 사용됩니다.
값타입(struct)의 경우 값을 복사하여 값의 참조를 전달하는게 아닌 실제 메모리에 저장된 "값"을 복사하게 됩니다.
뭐 여기까진 모두가 아는 사실.
여기서부터 COW의 내용이 나오게 됩니다.
매번 모든 값을 복사할 경우 원본의 변경된 값이 없음에도 매번 새로운 공간을 할당하여 불필요한 메모리의 낭비가 발생할 수 있습니다.
var str1 = "ABC"
var str2 = str1 // str1(원본)의 값이 다르지도 않은데 메모리 공간을 새로 계속 할당해야할까?
이런 일을 방지하고 메모리를 효율적으로 사용하기 위해 사용되는 것이 Copy on Write 입니다.
Copy on Write은 데이터 복사시에 실제 값을 복사하지 않고 동일한 값을 참조(공유)하도록 합니다. 그리고 값이 변경(Write)될 때 원본의 값을 복사해 변경을 적용하게 됩니다.
장점만 존재할까요?
CopyOnWrite를 사용하지 않는다면 메모리의 단일소유만을 고려해 참조 횟수를 계산할 필요가 없을테지만,
CopyOnWrite를 사용한다면 생성되는 사본의 수를 확인하기 위해 참조되는 횟수를 계산해야 합니다.
?? 값타입에 참조횟수가 ????? 라는 의문이 ? 그리고 참조횟수를 계산해야한다?? 라는 의문이 생겼습니다. 아래에서 정리해보겠습니다.
여기서 "참조되는 수를 계산해야 합니다" 라는 말의 의미를 알아봅시다.
설명해보자면
1. 만약 참조횟수가 1이라면 해당 Data는 한 곳 에서만 소유하고 있으므로 안전하게 수정될 수 있습니다.
var array1 = [1, 2, 3] // array1의 참조횟수: 1
2. 그러나 참조횟수가 1보다 크다면?
var array1 = [1, 2, 3] // array1의 참조횟수: 1
var array2 = array 1 // array1의 참조횟수: 2
3. 이런 경우 = 참조횟수가 1보다 크다면? 변경하기 전에 복사본을 만들어야 한다고 컴파일러가 알 수 있습니다.
var array1 = [1, 2, 3] // array1의 참조횟수: 1
var array2 = array 1 // array1의 참조횟수: 2
array2.append(4) // 변경이 발생했으므로 array1을 다른 메모리 공간에 복사하고 변경해야돼!!
array2는 array1과 동일한 메모리 주소를 공유하다가.
array2가 변경된 순간 cow가 발동하며 array1로부터 독립적인 복사본 데이터를 만들고 메모리에 할당합니다.
그 후 데이터를 변경합니다.
여기부터 여러 궁금증이 생겼습니다.
array(값타입)는 참조횟수(RC)를 사용하나? 고로 주소공간 공유하나? 참조타입인가 등등...
하나씩 알아봤습니다.
우선, 우리가 알고있는 array(값타입) 같은 Swift의 기본 컬렉션에서 사용되는 참조횟수는 Class에서의 Reference Count(RC)와 비슷하게 들릴 수 있으나 기능, 존재 이유, 메커니즘이 분명 다릅니다.
모두 아시겠지만 참조타입의 경우 클래스 인스턴스를 가리키는 모든 참조에 대해 RC를 관리하고 RC가 0이 되면 메모리에서 해제됩니다.
COW에서의 참조횟수는 효율적인 복사를 지원하기 위해 사용됩니다.
또한 다른 궁금증 들을 설명해보자면..
Swift에서 Array나 String들은 분명 "값타입"으로 동작합니다.
따라서 변수나 함수로 전달 시 값이 복사됩니다.
하지만 모든 상황에 깊은 복사(Deep Copy)가 일어나진 않는다고 합니다.
또한 Array나 String같은 컬렉션들은 내부적으로 "힙영역"에 저장될 수 있다고 합니다. // ???
Swift는 성능을 위해 Array나 String같은 값타입이 실제로 내용이 수정되지 않는다면 실제 데이터의 복사는 이루어 지지 않고 내부 데이터를 참조하는 포인터를 복사한다고 합니다. ( 위의 Cow에서 나온 같은 내용이네요. )
결론적으로 Array, String등 Swift의 기본 컬렉션들은 개발자에게 값타입처럼 보이고 값타입으로 동작하지만,
컴파일러 단에서 효율적인 메모리 관리를 위해 참조타입의 성질을 일부 활용한다고 하네요.
여기까지 생각해보니 함수에서 받게되는 파라미터가 상수로 사용되는 이유를 알거같네요.
파이썬에선 변수 아니였나..?