개발을 시작하는 이야기

Multi Thread를 구현해보자 (3) 본문

개발 이야기/우리동네 문화유산 :: JHeritage

Multi Thread를 구현해보자 (3)

Teiresias 2022. 4. 28. 22:26

오늘은 지난번에 이야기한 대로 Process의 구조에 대해서 좀 더 알아보려고 한다.

Code

 프로그래머가 작성한 소스 코드가 기계어 형태로 저장된다. 기계어라 함은 컴퓨터가 읽을 수 있는 가장 밑단의 언어로 0과 1로 이루어진 언어이다. 컴파일 타임에 결정되고, 중간에 코드가 변경되지 않도록 Read-Only 형태로 저장된다.

Data

 전역변수와 static 변수가 저장되는 공간이다. 프로그램 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리가 해제된다. 실행 도중 변수 값이 변경될 수 있기 때문에 Read-Write로 저장된다.

struct Profile {
	static let country = "Korea"
}

var name: String?
var age: Int?

func fetchData() {
	
}

위의 예시를 보자면 country는 static 변수로 데이터 영역에 할당되고, name과 age는 전역 변수로 마찬가지로 데이터 영역에 할당된다.

Heap

 ARC에 의해서 Reference Counting이 관리되는 영역이다. Code, Data, Stack중 유일하게 런타임시 결정되기 때문에 데이터의 크기가 확실하지 않은 가변적인 데이터 타입들이 할당된다. 언어마다 다르지만 스위프트에서는 Class의 인스턴스, Closure, 그리고 가변적인 String, Array 같은 타입들이 Heap 영역에 저장된다. 

 

 Heap의 장점은 메모리 크기에 제한이 없다는것, 본질적인 범위가 전역이기 때문에, 프로그램의 모든 함수에서 액세스 할 수 있다는 점이 있지만, 그만큼 할당작업과 해제 작업으로 인한 속도 저하와, 이중 해제, 해제 후 사용 등으로 인한 힙 손상 작업으로 인한 속도 저하, 두 개 이상의 쓰레드가 동시에 접근하려 할 때 Lock이 걸림으로 인한 속도 저하 등이 있다. 명확한 단점인 속도 저하는 엄청 느려진다거나 하는 것이 아닌 Stack에 비해 상대적으로 느려진다는 것이다.

Stack

함수 호출 시 함수의 지역변수, 매개변수, 리턴 값 등이 저장되고, 함수가 종료되면 저장된 메모리도 해제된다. 컴파일 타임에 결정되기 때문에 무한히 할당할 수 없다. 스택은 프로그램이 자동으로 사용하는 임시 메모리 영역이다. 프로그래머가 함수를 호출하면 OS는 내부적으로 함수 안에 선언된 파라미터, 지역변수 등을 스택에 할당해주는 것이다. 그리고 함수가 종료되면 스택에 저장된 메모리는 반환된다.

 

또한 스택은 LIFO(Last In First Out, 먼저 생성된 변수가 가장 나중에 해제됨) 데이터 구조이고 CPU에 의해 관리되고 최적화되기 때문에 속도가 매우 빠르다. 하지만 지역 변수만 액세스가 가능하고 메모리 크기에 대한 제한이 있어 무한히 할당할 수 없는 단점이 있다.

struct Profile {
	static let country = "Korea" //static 변수로 Data에 할당
}

var name: String? //전역 변수로 Data에 할당
var age: Int? //전역 변수로 Data에 할당

func fetchData(_ a: Int, _ b: Int) -> Int { //파라미터 a, b는 Stack에 할당
	let sum = a + b //지역 변수로 Stack에 할당
    let result = sum * a //지역 변수로 Stack에 할당
    return result
}

그동안 모르고 작성했던 코드들이 대충 알아서 메모리의 Data영역과 Stack 영역에 지정되었다가 해제되는 거였다.

그럼 이제 언제 Heap을 사용하고 언제 Stack을 사용해야 할까?

위의 장단점에서 알 수 있듯 Stack은 메모리가 한정되어 있기 때문에 너무 큰 메모리는 할당할 수 없다. 때문에 데이터의 크기를 모르거나, Stack에 저장하기엔 큰 데이터의 경우에는 Heap에 할당해주어야 한다. 만약 스택에 너무 많은 메모리를 할당하게 되면 스택 오버 플로우가 발생한다. 

그 스택 오버플로우가 이 스택 오버플로우가 맞다.

스택에 너무 많은 메모리를 할당하게 돼서 자신의 스택 영역을 초과하는 경우 앱이 죽어버리게 된다.

힙과 스택의 메모리 공유

위 내용들을 보면서 무의식적으로 생각한 것은 메모리의 영역을 서로 나누어서 할당받아 사용할 거라고 생각을 했는데 사실 힙과 스택은 같은 메모리 영역을 공유한다!

힙은 낮은 메모리 주소부터 할당받고, 스택은 높은 메모리 주소부터 할당받는 것이다. 같은 메모리 공간을 공유하기 때문에 스택과 힙이 영역 외로 확장되려고 하면 오버 플로우가 발생하게 되는 것이다. 

 

사실 원래 ARC에 대해서 공부하기 전에 알아야 할 사전 지식이라고 하는데, ARC보다 나중에 살펴보게 되었지만 둘 다 어느 정도 정리를 할 수 있었다. 다음에는 이제 실질적으로 프로젝트에 입혀보면서 더 알아보는 시간을 갖도록 하자.

 

https://teiresias.tistory.com/21

 

ARC(Automatic Reference Counting)

ARC(Automatic Reference Counting) iOS는 앱의 메모리 사용을 추적, 관리하기 위해 ARC 기능을 사용한다. ARC는 Automatic Reference Counting의 줄임말로 참조 메모리 관리를 자동으로 해주는 기능을 뜻한다. 인..

teiresias.tistory.com