관리되는 힙(Managed Heap)
메모리 관리자가 자동으로 관리하는 메모리
간단하게 말하면 Unity에서 동적할당해서 생성된 오브젝트들은 관리되는 힙에 할당된다.
유니티의 관리되는 힙(managed heap)은 축소보다 더 쉽게 확장될 수 있다. 그러니까 메모리를 먹는 괴물이 되기 쉽다는 뜻이다.
유니티의 가비지 컬렉션 전략은 메모리 단편화가 일어나는데 이로 인해 크기가 커진 힙이 줄어드는 것을 방해할 수 있다.
흰 박스 : 관리되는 힙에 할당된 메모리 용량
주황, 분홍, 파랑 등의 박스 : 힙에 할당되어있는 데이터 값들
위 그림에서 비어있는 흰색 영역이 할당 가능한(사용 가능한) 힙 영역이다.
만약 할당하고자 하는 데이터가 들어갈 공간이 없다면 관리되는 힙의 전체 크기를 늘린다.
Unity의 가비지 컬렉션은 Boehm GC 알고리즘을 사용한다.
이 알고리즘은 세대 기반 가비지 컬렉션 방식이 아니고, 압축도 하지 않는다.
비세대 기반이기 때문에 가비지 컬렉팅이 일어나면 모든 힙 영역을 정리해야만 한다.
그렇기 때문에 힙이 클수록 GC의 성능이 떨어지는 것이다.
힙 영역의 오브젝트가 해제되면 그림의 빨간 원처럼 여유 공간이 생긴다.
힙의 여유 공간보다 큰 오브젝트가 할당되어야 할 때
일단 한 번 힙이 확장되면 힙의 여유 공간이 많이 있다고 하더라도 잘 축소하지는 않는다. 왜냐하면 후에 좀 더 큰 메모리 할당이 필요하게 되었을 때 축소했던 힙을 다시 확장하지 않기 위해서다.
힙이 축소될 경우에 줄어든 만큼의 힙을 다시 OS에게 돌려주긴 하는데 언제 해제가 될진 잘 모르니까 너무 믿지는 마라.
관리되는 힙이 사용하는 주소 공간은 ‘절대’ 운영체제에 반환되지 않는다.
32 비트 프로그램에서 힙의 확장과 수축이 반복해서 일어나면 주소 공간이 부족해질 수 있다. 이럴 경우 OS는 프로그램을 종료시킨다.
하지만 64비트는 주소 공간이 충분하기 때문에 프로그램을 평생 실행시켜도 문제없다.
유니티의 가비지 컬렉팅 방식은 구리다.
그리고 아주 구시대적인 방법이다. 이미 마이크로소프트 .Net의 CLR은 가비지 컬렉션 방식을 점차 발전시키며 세대별 가비지 컬렉션, SOH, LOH 의 구분 등을 적용시켰다.
하지만 유니티는 MONO가 만들어진 시절의 가비지 컬렉팅 방식에서 조금 진화해서 가비지 컬렉팅을 한 프레임에 모두 처리하는 게 아닌 점진적 방식 즉, 여러 프레임에 나누어 가비지 컬렉팅을 조금씩 하도록 바꾸었지만 근본적인 해결은 하지 못했다.
메모리 할당도 간단히 말하면 크기가 크던 작던 모든 오브젝트를 같은 힙에 때려박고 공간이 부족하면 모든 힙 영역에 대해 가비지 컬렉팅을 해보고 그래도 안되면 힙을 두 배 늘려서 때려박는 식이다.
그리고 어느 시점에 가비지 컬렉팅이 되는가 역시 확실하게 장담할 수는 없다.
기본적으로는 힙 메모리가 한계에 도달하거나 새로운 오브젝트를 할당해야 하는데 공간이 부족할 때 일어난다고 예측은 할 수 있지만 이 외에도 플랫폼, 구동 환경, 여러 어플 사용 등으로 인해서 GC가 일어날 수 있다. 그리고 이 때마다 프레임 드랍이 일어날 수 있다.
GC로 인한 부하가 걸리면 프로파일링을 통해 GC를 손수 최적화 시켜줘야 한다.