컴퓨터/C#

| 니앙팽이 - C# | 1 | 메모리 [ Stack&Heap / Value&Reference Type / Struct&Class / Boxing&UnBoxing / Garbage Collector ]

씨샵

🧑🏻‍💻 1. Memory

메모리 : 란 실행된 애플리케이션이 상주하는곳
CPU : 명렬어 처리를 위한 하드웨어

1. Stack과 Heap에대해 설명해보시오 
2. Stack과 Heap이 어디에서 저장되는가?
3. 어떤것이 Stack에 저장되고 어떤것이 Heap에 저장되는가?
4. Stack과 Heap 사이즈에 대해 설명해보세요
5. Stack과 Heap메모리의 Deallocate 방식에 대해 설명하세요

6. 원시데이터과 참조데이터 은 메모리 각각에 어디에 저장 되는가?
7. 원시데이터도 Heap에 저장될 수 있을까?
8. string은 Stack에 저장될까, Heap에 저장될까?

9. 값타입과 참조타입에 대해 설명해보세요
10. Call By Value & Call By Reference
11. 값 복사, 참조 복사에 대해 설명해보세요

12. 구조체와 클래스에 대해 설명해보세요
13. 구조체는 Stack에 저장될까 Heap에 저장 될까?
14. 구조체는 Heap에 생성 될 수 있나?
15. 그럼 전달에 있어서 값전달인가? 참조 전달인가?

16. 박싱과 언박싱을 해결하는 방법이 있을까요?
17. 박싱과 언박싱에 대해 설명하세요 그리고 그 성능에 대해 설명해보세요

14 가비지 컬렉터

📄 1. Stack과 Heap

1. Stack Heap

1 뭘 저장하나?, 2 속도 (접근, 생성해제)?, 3 데이터 접근법, 공유 범위?, 4 최대 할당 사이즈는?, 5 메모리 관리

비교 Stack : 정적 메모리 Heap : 동적 메모리
① 저장 하는 것 함수내 지역변수, 매개변수, this 매개변수
참조 변수(주소값)
함수/메소드 호출 정보(Stack프레임)
객체 그 자체 저장(객체 멤버 변수들도) (new)
② 속도 (생성과 해제 & 데이터 접근) 빠르다. 느리다
③ 접근 방법과 공유 가능성 1. LIFO 순서
2. 함수내 블럭에서만 접근 가능
2. 무작위 접근
2. 프로그램간 공유가 가능하다.
④ 최대 할당 사이즈 1. 컴파일 타임에 사이즈 제한
2. arr[]와 같은 문법
3. 약 1M 혹은 500M정도 작다.
1. 고정 크기가 없지만 런타임에 결정
2. *arr & malloc 와 같은 문법
3. 제한이 거희 2GB도 가능하다.
⑤ 메모리 관리 할당과 해제 자동 관리 블럭 내에서만 사용 가능
Stack 메모리는 빠르게 할당되고, 함수 종료시 자동으로 해제된다
1. UnMannaged 직접 관리 :
프로그래머가 직접 Heap 메모리 해제를 관리
2. Managed 자동 관리 :
가비지 컬렉터가 메모리의 참조를 검사하여 자동으로 관리

2. 그 외

  • Data Memory : 프로그램이 종료될 때까지 지워지지 않을 데이터 저장
  • Static Memory : 전역 변수, static, 상수

📄 2. Managed / Unmanaged

이 둘의 차이는 프로그래머가 하드웨어의 자원을 직접 제어할 수 있는지의 여부를 말한다.

① Unmanaged

  • C/C++ 대부분의 라이브러리가 Unmanaged다.

  • 소멸자와 스마트 참조 변수(주소값)같은것을 사용해 메모리 Leakage를 관리 해줘야 한다.

  • C# 에서는 I/O FileStream, Database, Network같은 외부 자원들

② Managed

  • C#, Java같은 언어가 Managed 언어다. 가비지 컬렉션으로 런타임이 알아서 메모리 관리를 해준다.
  • C# 에서는 .NET 에서 생성한 모든 객체

1. Managed Heap
2. Managed Heap
3. Managed Heap


📄 3. 값 타입 / 참조 타입

1. 값/참조타입

비교 값 타입 참조 타입
① 해제 Stack에서 해제 Heap에서 가비지 컬렉터로
② 종류 구조체, 튜플
정수, 부동소수점, Bool
객체, Class, Object
String, Array,
Interface, Delegate, Exception
③ 대입시 동작 할당, 함수 인수, 리턴시 복사됩니다. "참조 변수(주소값)"을 리턴
④ 상속 상속 받을 필요 없다 상속을 받는 기능 존재
⑤ Equals 모든 필드의 값들을 비교
struct나 다른 값 타입을 정의시
연산자 오버로딩으로 == & != 구현
참조 변수(주소값) 주소를 비교
**‼ 중요한점은 "참조 변수(주소값)" 이녀석은 스택에 저장되지만 값타입은 아니다 ‼**

2. C#의 String

  1. string은 참조타입임에도 값 복사로 전달이 된다.
  2. 그런데 가비지 콜렉터에 의해 소멸 된다.
  3. Immutable
  4. = 연산자시, 내용이 바뀌는것이 아니라 새로운 문자열이 생긴다.
  5. == != 문자열 비교에서 일반적인 참조차입의 비교와는 다른 로직으로 구성되어있다.
    • 일반적으로는 "참조 변수(주소값)"을 비교하여 주소값이 다르면 다른것,이지만.
    • heap 주소값이 다르더라도 내용이 완전히 같으면 같다고 판단한다.

📄 4. Struct / Class

1. 공통점

공통점 Struct Class
① filed, method 가질 수 있음 가질 수 있음

2. 차이점

차이점 Struct Class
① 메모리 할당 방법 기본적으로 값 형식은 Stack에 할당되지만,
참조형식에 내포된 "인스턴스 값" 형식은 Heap에 들어감
Heap
② value & ref type Value Reference
③ 멤버들의 기본 접근자 Public Private
④ 매개변수 전달 방법 Call By Value : 복사전달
Stack에 새로 복제본이 생김
Call By Reference : 참조 변수(주소값)
⑤ 소멸자 사용 여부 사용 불가능 사용 가능
⓺ 목적에 따른 코딩 스탠다드 룰 아무 함수 없이 순수하게 데이터 집합 멤버 변수 & 메소드

3. 구조체의 중괄호 초기화

EventReference { Path = path, Guid = GuidLookupDelegate(path) };
pair {first = f, secont = s};

Struct, Class 메모리 할당


📄 5. 인자 전달 방법

A. 값을 값으로 전달하기

그냥 넣기

B. 값을 참조로 전달하기

ref로 넣기

C. 참조형식을 값으로 전달하기

인스턴스 주소의 복사본을 수신할 수 있다.
두 주소가 동일한 개체를 참조한다.

void IntByReference(int[] _Nums) => _Nums = new int[] {1,2,3,4,5,6,6,7,8,9,10};

    다음과 같은경우는 참조형식이 값으로 전달된 사례다.
    즉 인수로 들어간 _Num이랑, 받아온 _Num이랑은 다른 주소를 가진다.
    다만 동일한 개체를 참조한다.
    위와 같은 경우는 _Num은 인수로 들어간 _Num에게 영향을 끼치지는 않는다.

    다만 다음과 같은 경우는 조금 다르다.

void IntByReference(int[] _Nums) {
    _Nums[0] = 1; _Nums[1] = 2; _Nums[2] = 3; 
    _Nums[3] = 4; _Nums[4] = 5; _Nums[5] = 6; 
    _Nums[6] = 7; _Nums[7] = 8; _Nums[8] = 9; 
    _Nums[9] = 10;
}
    다음과 같이 비록 인수로 들어온 배열 자체는 참조가 맞지만,
    배열을 이루는 각각 원소 하나하나는 값이므로 
    실제 _Num의 값에 영향을 미친다. 이렇게 값 하나하나에 영향을 미치는 함수를 실행하지만
    인수로 들어간 값을 변경하고 싶지 않을때는 Clone을 해야되겠다.
D. 참조를 참조로 전달하기

그냥 넣기


📄 6. 박싱 / 언박싱

런타임에 타입을 결정하는 기법

① 박싱 : 값 타입 -> 참조 타입으로 변경

  • Ex). ArrayList에 Struct대입시 박싱이 일어나 Struct는 값타입에서 참조타입으로 변해 Heap에 쌓이게 된다.
    1. 그 과정은 Stack에 저장된 값을 Heap으로 복사를 하고,
    2. 메모리의 크기는 값 타입 내에 들어있는필드들의 메모리 크기에 더하여
      다른 Heap의 객체들처럼 타입 객체 참조 변수(주소값)도 포함
  • 따라서 가비지 컬렉터의 대상이 된다.

② 언박싱 : 참조 타입 -> 값 타입으로 변경

  • 캐스팅을 통해 다음 과정이 진행된다
    1. 타입에 맞는 필드들의 주소를 가져온다. (언박싱)
    2. 주소에 있는 필드들의 값을 Stack의 값 타입 인스턴스 쪽으로 Heap에서 복사된다.

비용이 크기 때문에 가급적 사용하지 말아야 성능에 무리가 안간다.
박싱, 언박싱 과정을 통해서 Heap에 가비지가 쌓여 GC에 무리를 줄 수 있는 작업이기 때문이라

이를 해결하기 위해서는 가급적 제네릭을 활용해 줘야 한다.

③ as 연산자 : 참조 타입 -> 하양 참조 타입 보통 캐스팅 대신에 사용되는 더 안정적인 연산자

  • 하양캐스팅수행 (만약 실패시 Null 반환)
    1. 다만, 언박싱의 캐스팅과 다른점은 참조타입간의 캐스팅만 가능, 값타입으로 변하는 언박싱에선 캐스트를 사용해야함

박싱/ 언박싱
as 연산자
is, as


📄 7. Garbage Collector

Managed Programing Language가
Heap(동적 메모리)에 할당된 메모리를 *자동* 해제하는 방법

1. 가비지 : 쓰레기 객체

Counter c = new Counter();

    c : Heap(동적 메모리)에 존재하는 Counter 객체를 가르키는 참조 변수(주소값) 
        참조 변수(주소값)Stack(정적 메모리)에 존재한다.

    new Counter() : Heap(동적 메모리)에 할당(점유)하고 있는 Counter 객체
        이 겍체는 Heap(동적 메모리)에 존재한다.

    c와 new Counter()은 서로 한몸인데... 만약 아래와 같이 참조 변수(주소값)가 NULL 참조 변수(주소값)를 가르키게 된다면?

c = null; // 이때부터 new Counter()은 가비지에 처리 대상이 된다.

만들어 놨으면 Heap에 쌓이는데.. 정작 그 Heap을 참조하는 변수가 없어서 생기는 문제.
메모리 공간이 할당되었지만 레퍼런스를 모두 잃었기 때문에, 더 이상 그 메모리 공간을 사용할 수 없는 상태가 된 것임.

2. 가비지 콜렉터

"객체를 가르키는 레퍼런스 변수를 잃어버려 사용되지 못하는 메모리" 를 찾아서 자동으로 해제(관리) 해주는 프로세스다.

  • ① 작동 타이밍.
    1. Heap 부족 : 인스턴스 메모리 할당을 수용할 수 없을 정도로 Heap 메모리가 충분하지 않으면 가비지 컬렉터를 실행한다.
    2. 메모리 단편화 : 단편화 현상이 빈번하여 실질적인 빈 공간은 있지만, 뜨문 뜨문 작디 작은 데이터만 들어갈 수 있는 공간이 산재해 있다면. GC가 자주 발생할 수 있다.
      • 이러한 이유로 Unity DOTS 가 주목받고 있다.
      • 왜냐 하면 DOTS의 기능중 메모리 단편화가 발생하지 않는 아키텍쳐(데이터 지향 아키텍쳐)로 구현되어 있기 때문이다.
  • ② 알고리즘
    • Mark and Sweep : CPU 자원을 요구하는 작업이다.
      1. Root(참조 변수(주소값) 변수, 함수)로 부터 참조된 Heap의 모든 오브젝트를 순회하며
      2. Root으로 부터 순회 했던것은 "Mark" 즉, "Mark가 된것은 참조가 있다"는 의미다.
        레퍼런스가 없는 오브젝트들은 "UnMarked"
      3. Unity는 "UnMarked(레퍼런스가 없는)" 오브젝트를 "Sweep"을 통해 삭제하여 메모리를 확보합니다.
    • 상호 참조
      • 상호 참조중인 두 객체중 하나라도 참조가 된다면 Mark, 그렇지 않다면 Sweep을 통해 삭제

3. 최적화

  • ① 객체 풀링 : 메모리를 해제하는 것이 아닌 재활용을 한다
  • ② 올바른 스트링 연산 || StringBuilder 사용
  • ③ 값 타입을 사용할 수 있으면 최대한 그렇게 하거라.

4. 여담

  • GetComponent 메서드는 에디터에서 실행될 때 항상 메모리를 할당하지만,
    빌드된 프로젝트에서는 할당하지 않습니다.

참조

  1. C# Interview Questions : Stack Heap | Boxing Unboxing | Value & Reference Type