1.컴퓨터 
저장 장치
하드디스크 -  컴퓨터에 물리적으로 파일을 저장
메모리 - 프로그램/어플리케이션이 실행하는데 필요한 데이터를 저장 
ex) 게임 - 레벨,소유골드 등등...

메모리는 배열처럼 생겼다 (배열은 아님!!!!)
Code : 프로그래머가 작성한 코드를 보관
Data : static,const 어플리케이션 전반에 필요한 데이터 저장
Heap : 참조 데이터 (객체) 저장
Stack : 로컬변수 ,매개변수 저장
어플리케이션 실행 순서에 필요한 데이터 보관

메모리는 2진수로된 주소값이 있다.
해당 메모리에 접근하기 위해 주소값을 가지고 있으면 된다 -> C/C++의 포인터 기능

int x = 10; 
해당 데이터(10)는 stack영역 메모리 첫번째에 저장된다.
메모리 첫번쨰는 0000 0000주소값을 가지고있는데

해당 데이터에 접근하기 위해 0000 0000 주소값을 가져오는게 아닌 x로 변경하여 사용하게 되는것이다.
x도 stack 영역에 저장됨

= : Rvalue를 Lvalue로 옮겨주는것이다.

class는 참조값이기에 heap 영역에 추가됨
heap은 어디에 저장되는지 모름, 그리고 알 필요도 없기에 빈 어딘가에 저장된다고 생각하면 됨

Class Person 
Class Data는 heap영역에
Calss로 만들어진 객체는 Stack 영역에 저장됨

사용할떄 주의할점
Stack 영역
변수할당시 생성
함수가 끝날때 삭제

참조가 한번 끊어지면 다시 heap영역의 data에 접근이 되지 안흔ㄴ다 -> heap영역의 과잉생산 -> 메모리 누수

데이터가 있지만 활용하지 못하는것 -> 쓰레기(가비지)
데이터가 언제 삭제되는지 모름, 단 한 조건은 있음
데이터가 사용되지 않을때 (참조가 끊겼을때)

GC가 언젠가 쓰지않는 데이터를 호출하여 처리해줌

가비지가 생긴다고 했을때
가비지를 1개만 가져가면 괜찮지만
가비지를 100개 1000개씩 한번에 가져갈때
컴퓨터가 과부하가 걸린다 -> 게임시 렉이 걸리는 이유

효율적으로 쓴다 = 가비지를 최대한 적게 생성시킨다.

Heap영역
new를 통해 생성 GC로 인해 삭제
Unity에서는 Instanitate() , Destroy()
하지만 Destroy()에서는 오브젝트가 삭제되는거지 Data는 남아있어 가비지로 남아있음 

유니티 inspector에 들어간 컴포넌트의 상위는 Class ->Monobehaviour 이다. 즉 ,  Instanitate() 를하면 new를 통해 생성하는것과 똑같은 의미이다.

최적화 -> 반복적으로 new 하는것을 줄인다
(Update,While 등등...)

최적화 1.
데이터를 한번 준비하고 **재활용**
-> 변수들을 미리 선언하여 호출하여 쓰기 

Unity에서 Vector는 구조체이기 때문
C/C++ 에서는 구조체나 class는 비슷하다.
(왜냐하면 프로그래머가 직접 메모리 할당 및 해지 , 포인터를 이용한 주소값 접근을  담당하기 때문)

하지만 C#은 다르다.
구조체는 데이터 자체가 Stack영역에 생성됨
즉, 구조체는 가비지가 남지 않게된다.
Stack 메모리가 heap 속도보다 더 빠르다
즉, Vector는 자주 사용되기 때문에 구조체로 Unity에서 만든것이다.

string str = "유저 레벨"

유 저  레 벨 이라는 5가지 저장됨

문자열은 불변객체 = 변하지 않는다는 뜻

str += "chad" 가 실행되면

내부적으로 간단하게
유저 레벨 뒤에 chad가 붙는것이 아니다.
즉, 유저 레벨 뒤 heap영역 메모리 데이터에 다른 데이터들도 있기에 붙일수가 없기 때문이다. 

유저레벨 + chad를 합친 문자열을 다른 heap영역에 새로 만들어진다. 

즉, heap에는 유저 레벨 , 유저레벨 chad 데이터가 2개가 동시에 존재하는 것이다
즉 string의 += 연산자를 계속 쓸때마다 heap영역을 쓰는 것이고 추가가 되어진다. 

이런 현상을 "파편화"라고 한다.

문자열 대처법
1.string을 한번에 제작 [ 보간문자열,스트링포맷,+연산자]
(추천)1-1.보간 문자열 : 문자열 앞에 $ 표시를 붙여 사용한다.
1-2.string.Format
1-3.+연산자를 한줄에 적어버리기

문자열을 한번에 제작하지 못하는경우
2.StringBuilder 사용하기

근데 왜 StringBuilder는 왜 이렇게 사용해도 괜찮은가??
ex)
유저 레벨 이 5글자이면 , StringBuilder는 추가로 공간을 확보해놓고 해당 확보한 공간을 넘어서는경우 새로 ㅏㄴ들게 된다. -> C#의 List와 유사하다.

stringBuilder는 처음에 16글자
넘어가면 2배인 32글자를 새로운 heap영역에 할당 이런식으로 된다.

stringBuilder는 일반 문자열보다 느리지만 파편화를 덜 방지 할 수 있기에 사용이 추천된다.

StringBuilder도 처음 초기화시 크기를 선언하고 사용 할 수 있다.

웬만한 문자열은 보간문자열을 사용할 수 ㅣㅆ고
중간중간 문자열이나 Data를 실시간으로 조합할때는 StringBuilder를 추천한다.
(개별 입력으로 받아들여서 합치는 경우에는 StringBuilder, 개발자가 처음 부터 값을 넣어주는 경우에는 $""를 쓰는 게 좋겠군요.)

Stack 값 형식 =  value
C#의 기본 타입(int,float,bool,char) , 구조체 , enum
Heap 참조형식 = referance
string class 배열 interface delegate

구조체는 Stack에 할당
메모리는 Stack에만 저장
큰 데이터나 외부 데이터를 가져와서 사용할떄 문제 발생

Calss는 Heap에 할당
Class의 객체(변수)는 stack에
Class 객체의 data는 heap영역에
데이터 접근은 Stack에 접근후 데이터의 주소를 파악후 heap영역에 접근한다. 

C#에서는 Class를 만들떄 new 어트리뷰트를 사용하여 생성한다. 유니티는 Instaiate는 new키워드와 유사한다

Static 키워드가 변수 ,메서드가 있으면
외부의 클래스의 이름을 바로 호출하여 바로 사용 할 수 있다.

Static을 메모리 관점으로 봤을때
Data영역에 배치 되어있음 
Data 영역은 프로그램 시작시 생성
프로그램 종료시 삭제

즉 , Static 키워드는 Data영역에 접근하여 언제든지 호출이 가능해진다.

Static 키워드 데이터는 Scene 사이의 데이터가 유지되어있다.

Static Class는 데이터를 세팅해줘야 됨
나머지 int,float은 언제든지 쓸 수 있음 
객체도 Class라서 초기값은 null이다.

Static 키워드들이 heap,stack영역 데이터에 접근해서 사용 할수는 없다.

'C#' 카테고리의 다른 글

Dirty Flag Pattern  (0) 2026.02.03
C# :: 메모리 구조  (0) 2024.10.14

Dirty Flag Pattern

'C#' 카테고리의 다른 글

C# 메모리 구조 간단 작성  (0) 2026.03.14
C# :: 메모리 구조  (0) 2024.10.14
Code 영역 : 프로그래머가 작성한 코드를 보관  
Data 영역 : Static, Const
프로그램/어플리케이션 전반에 필요한 데이터 저장
생성 시점 : 프로그램/어플리케이션 실행시
삭제 시점 : 프로그램/어플리케이션 종료시
Heap 영역 : 참조 데이터(객체) 저장 생성 시점 : new(C#), Instaniate(Unity)
삭제 시점 : GC(가비지 컬렉터) 동작 시점
가비지는 해당 데이터가 사용하지 않는 시점
(참조가 끊긴 시점)GC에 수집됨

p.s
Unity Editor의 Destroy()라는 오브젝트를 삭제해주는 메서드가 있는데 이것도 눈에 보이는 오브젝트가 삭제된거지 Heap영역에 있는 Data들은 가비지 데이터가 되어버린다.
Stack 영역 : 로컬 변수,매개 변수 저장
프로그램/어플리케이션 실행순서에 필요한 데이터 보관
생성 시점 : 함수 내에서 선언시, 변수 할당시 생성
종료 시점 : 함수가 종료될때

 

priavte void Start()
{
    int x = 10;
    int y = x;
    x = 5;
}

 

해당 코드가 있을때 x,y의 최종 데이터는 어떡해 되는가?

x = 5 , y = 10이 될것이다.

 

class Person()
{
	public int Age;
	public string Name;
}

private void Start()
{
	Person personA = new Person(32,"BirdHead");
    
	Person personB = personA;
    
	personA.Age++;
}

해당 코드에서 PersonA와 PersonB의 Age의 데이터는 몇인가?

결과는 둘다 33 이다.

 

Q.왜 위의 코드와 아래의 코드의 데이터는 왜 다른식으로 저장돼?

A.

로컬 변수(지역변수) 및 매개변수는 Stack영역에 첫 메모리부터 저장되기 시작한다.

해당 데이터에 접근하기 위해 0000 0000 주소값을 가져오는게 아닌 변수 x로 이름을 대체하여 사용하게 되는것이다.

해당 변수(주소 이름)을 호출하여 그 안에 데이터를 꺼내서 사용하는 것이다.

 

 

해당 코드는 Class로 객체 A,B를 생성하고 A의 Age 데이터를 1 증가시키는 코드이다.

1번 : 체(인스턴스)는 Stack 영역에 저장되고 객체의 데이터들은 Heap영역으로 저장된다.

이때 Heap의 저장된 Data는 Stack에 참조 되어 있는 상태라고 표현한다.

2번 : B 객체에 A를 대입 하게되면 , 객체인 B는 Stack 영역에 저장되고 B 객체의 데이터는 A에 참조되어 데이터를 사용하게 된다.

3번 : A 객체의 Age 데이터를 호출할때  Stack 영역의 A객체에 접근 후 참조된 데이터값에 접근하여 수정 할 수 있다.

 

이런 과정을 걸치면 B도 기존의 A데이터 Heap영역에 참조되어있기에 데이터가 변경되면 B도 같이 변경되는 모습을 볼 수 있다.

 

Q.객체를 삭제하게 되면 Heap영역의 Data는 어떡해 돼?

A.

Stack 영역의 객체만 삭제되고 , Heap영역의 Data는 참조가 끊기며 사용하지 않는 데이터가 된다.

즉, 가비지(쓰레기)데이터가 되어 메모리 누수를 발생시킨다.

하지만 C#에서는 이런 가비지 데이터들을 수집하여 처리해주는 GC(가비지 컬렉터)가 존재한다.

GC는 어느 시점에 동작하는지는 알 수 없다.

 

Q. GC가 있으니 메모리 걱정없이 사용해도 되는거 아닌가요?

A. 절대 아니다

private voud Update()
{
    Monster orc = new Monster();
    //Unity Editor인경우 Instaniate가 new와 똑같다.
    
    Debug.Log("몬스터 이름" + Orc.name);
}

===============================================================

IEnumrator Coroutine
{
	while(true)
    {
        Monster orc = new Monster();
        //Unity Editor인경우 Instaniate가 new와 똑같다.

        Debug.Log("몬스터 이름" + Orc.name);
        
        yield return new WaitForSeconds(1.0f);
    }
}

위에 적힌 두 코드들의 공통점은 반복되는 루프문이라는 것이다.

 

 

Update()는 1프레임에 한번씩 동작하는 함수이다. 60프레임이면 1초에 60번 동작하는 함수라는 것인데,

해당 함수는 Stack 영역에 객체를 생성하고 함수의 종료로 파괴되어 가비지 데이터를 생성하게되는데

지금 해당 함수로 1초면 벌써 가비지 데이터가 60개가 생성된것이다. 

 

그리고 GC가 호출되어 가비지데이터들을 수집할떄 수집 갯수가 많게되면 결국 우리가 잘아는 프리징,렉 이 발생하고 심각하면 프로그램이 종료되는 현상도 발생하는 것이다. 

 

유니티 오브젝트의 Inspector의 컴포넌트를 들을 보면 상위 클래스인 Monobehaviour로 구성된 것을 알수 있다.

즉, 객체 생성 후 파괴되거나 사라지게 되면 가비지가 생성 된다. 

 

유니티로 개발시에 오브젝트 생성 및 파괴를 목적이나 이유없이 절대 반복문에 작성하면 안된다.

 

Q. 그럼 가비지데이터를 생성 안하는 방법이 있나?

A. 아니 없다. 개발할때 무조건 가비지 데이터는 만들어 질 수 밖에 없다.

하지만 가비지 데이터를 적게 만들어내는 방법들이 있고 이런 방법들이 바로 최적화를 의미한다.

 

가비지 데이터를 적게 만들어내는 방법

1. 데이터를 한번만 준비하고 재활용 

public class MonsterDisplay : Monobehaviour
{

    private voud Update()
    {
        Monster orc = new Monster();
        //Unity Editor인경우 Instaniate가 new와 똑같다.

        Debug.Log("몬스터 이름" + Orc.name);
    }
}

============= 재활용 =======================

public class MonsterDisplay : Monobehaviour
{
    private Monster orc;
     
    private void Awake()
    {
    	//Awkae() 메서드로 오브젝트가 활성활될때 한번만 실행
    	orc = new Monster();
    }
     
    private voud Update()
    {
        Debug.Log("몬스터 이름" + Orc.name);
    }
}

위의 코드처럼 Monster 객체를 Class내의 변수로 선언하고 Awake()에서 한번만 생성 이후

Update()에서는 객체가 동작하는 코드를 작성하면 된다.

 

해당 코드에 내용 및 기능이 더 추가되면 오브젝트 풀링이라는 설계패턴이 만들어진다.

 

2.큰 규모의 데이터가 아니거나 자주 사용해야되는 데이터인 경우 구조체 사용하기

 

구조체는 데이터가 Stack 영역에 생성

즉 , 구조체는 파괴되어도 Stack 영역에서 파괴되기에 가비지 데이터가 남지 않게된다.

또한 Stack 메모리가 Heap 메모리보다 속도도 빠르기에 자주 사용되는 데이터들은 구조체가 최적화에 좋다.

 

Unity 내에서도 Vector2, Vector3는 Class가 아닌 구조체 타입이다.

 

그렇다고 모든 데이터를 구조체로 작성하면 문제가 생긴다.

1.큰 데이터를 구조체로 생성시 문제 발생

2.외부데이터를 호출해서 사용해야 될경우 문제 발생

이러한 문제점이 발생하기에 적절히 사용하여야한다.

Q.그럼 데이터들이 어떤게 참조되어 사용되는지? 어떤게 Stack에 만들어지는지 알아?

A.

Stack 영역 ( Value 형식 ) Heap 영역 ( Ref 형식)
C#의 기본 타입
(int,float,bool,char ….)
String
구조체 Class
enum interface
  Delegate

이 표를 통해 이렇게 나눠진다고 보면된다.

Stack 영역에 생성되는 데이터들은 Value(값) 형식이라고 부른다.

Heap 영역에 생성되는 데이터들은 Ref(참조) 형식이라고 부른다.

Heap 영역의 객체는 Stack에 생성되고 객체의 데이터는 Heap영역에 생성되고 객체에 참조되어진다.

 


결론 :

0, Stack 영역은 Value 형식들의 데이터가 저장 , Heap 영역은 Ref 형식들의 데이터가 저장됨

1. 구조체는 Value 형식 , Class Ref 형식이다. Ref 형식의 데이터들은 Stack 데이터들이 참조하여 사용되고 있다.

2. 객체 생성시에 미리 선언하여 저장해놓고 사용한다.

3.Ref 형식 데이터들은 참조가 끊기면 가비지 데이터가 되어 GC에 의해 처리된다.

'C#' 카테고리의 다른 글

C# 메모리 구조 간단 작성  (0) 2026.03.14
Dirty Flag Pattern  (0) 2026.02.03

+ Recent posts