메모리
환경
- 네이티브 환경 : C/C++이 속함. 메모리 할당과 해제를 직접 쌍으로 맞춰야 함
- 관리 환경 : C#이 속함. 메모리 해제는 가비지 컬렉터가 담당
값 형식과 참조 형식
- 값 형식의 데이터는 해당 데이터의 값이 스택에 저장됨 (struct형 데이터)
- 반대로 참조 형식의 데이터는 힙에 메모리를 할당 (class형 데이터)

Null
- c# 참조형 데이터에 null 값을 대입할 수 있도록 지원함
- 참조형 데이터가 초기에 null이라면 참조형이지만 힙 영역에는 생성되지 않음 (별도로 생성하거나 대입하지 않으면 null로 설정)
힙과 스택
- 프로그램 실행 시, 코드는 메모리에 적재되고 CPU에 의해 하나씩 순차적으로 읽힘
- 메모리는 코드와 데이터로 채워지고 힙과 스택은 데이터를 위한 메모리
- 스레드는 스택 영역은 별도로 가지지만 그 외의 영역(코드/데이터/힙)은 공유한다.
스택
- 스레드가 생성되면 기본적으로 1MB의 용량으로 할당
- 자료구조의 스택과 동작 방식이 동일
- 스택은 메모리 상으로 거꾸로 쌓임
- 스택에 쌓인 정보는 해당 영역을 벗어나면 자동으로 해제되지만, 재귀 호출로 인해 벗어나지 못하고 용량을 벗어나면 스택 오버플로우가 발생 (스택 오버플로우가 발생하면 오류 출력을 위한 메모리 할당 공간을 확보하지 못해 오류 출력도 못할 수 있음)
힙
- 별도로 명시하지 않으면 CLR은 관리힙을 가리킴
- 관리힙은 가비지 컬렉터가 할당/해제를 관리 (new로 생성된 모든 참조형 객체는 힙에 할당)
- 가비지 컬렉터는 일정 수준까지 메모리가 할당되면 동작함 (강제로 호출도 가능하지만 무거운 연산이기에 권장하지 않음)
박싱과 언박싱
- 박싱 : 값 형식을 참조 형식으로 변환
- 언박싱 : 참조 형식을 값 형식으로 변환
- 박싱을 일으키는 코드가 많아진다면 힙에 할당되는 메모리가 많아지기 때문에 박싱을 피하는 것이 좋음
object obj = 10; // 박싱
int num = (int) obj; // 언박싱
num = (Sum(5,6)); // int형 값 5,6이 object형으로 박싱
public int Sum(object a, object b)
{
int num1 = (int)a; // 언박싱
int num2 = (int)b; // 언박싱
return num1+num2;
}
가비지 컬렉터
- 관리힙을 관리하는 시스템으로 힙에 저장되는 객체는 0,1,2 세대로 나눠서 관리함
- 처음 생성되는 객체는 0세대로 취급
- 일정량의 메모리 사용 시, 참조가 해제된 객체의 메모리를 수거하고 남은 객체들은 1세대 올림
- 가비지 컬렉터가 작동하면 0세대만 확인하고 메모리가 부족하면 상위 세대까지도 확인
- 2세대 이후 메모리 공간은 시스템이 허용하는 한 계속 커짐
* 세대를 나눈 이유 : 0세대가 가장 많이 해제되는 점을 고려하여 구현한 시스템
대용량 객체 힙
- 큰 메모리를 가진 객체가 힙에 존재하면 매번 수거 과정에서 메모리 이동에 큰 부담이 생김
- 일정량 이상의 객체는 대용량 객체 힙에 할당 (다른 객체가 수거되도 메모리 주소가 바뀌지 않음)
- 이곳에 생성되는 객체는 처음부터 2세대임
- 객체가 수거되도 메모리 주소가 바뀌지 않기 때문에 메모리 파편화가 발생함 (주의해서 사용해야 함)