메모리와 런타임
20편
Content Directories 는 아직 로컬 전용이다
Unity 6.6 의 Content Directories 는 Player 에 넣는 로컬 콘텐츠만 다룹니다. 원격 콘텐츠를 AssetBundle 로 남기면 공유 에셋이 양쪽에 따로 빌드되고, Addressables 4.x 에서 그룹을 바꿀 때는 빌드 스크립트에 따라 경고 없이 빈 빌드가 됩니다.
Content Directories 의 참조 카운트는 핸들만 센다
Content Directories 는 Loadable 핸들과 씬만 세어 마지막 쪽이 놓은 1프레임 뒤 에셋을 내립니다. Instantiate 한 인스턴스는 세지 않아서, 쓰는 중에 핸들을 놓으면 인스턴스의 머티리얼이 경고 없이 사라집니다.
Content Directories 는 등록할 때 실선까지만 올린다
Content Directories 는 디렉터리를 등록할 때 매니페스트를 읽고 Root 와 실선만 올립니다. 나머지는 Loadable.Load 가 매니페스트로 파일을 찾아 실선 순서대로 열고, 씬과 핸들로 각각 올린 같은 에셋은 하나의 객체로 공유됩니다.
Content Directories 의 파일 이름은 내용 해시다
Content Directories 는 에셋 하나를 내용 해시 이름의 파일 하나로 빌드합니다. 바뀌지 않은 파일은 다시 쓰지 않아 재빌드가 65초에서 9초로 줄었고, 대신 Player 빌드에 스트리핑 정보를 넘기지 않으면 콘텐츠 전용 컴포넌트가 에러 없이 빠집니다.
Content Directories 는 Root 에서 참조를 따라 빌드한다
Content Directories 빌드는 Root Asset 에서 GUID 참조를 따라가며 닿는 에셋을 모읍니다. 빌드는 Loadable 같은 점선 참조까지 다 따라가지만 런타임 로드는 점선에서 멈추므로, 수명이 갈리는 자리에 점선을 둡니다.
Content Directories 는 번들을 에셋 단위로 쪼갠다
AssetBundle 은 번들 하나가 배포, 로드, 해제 단위라 에셋 하나를 쓰려 해도 번들을 통째로 엽니다. Unity 6.6 의 Content Directories 는 이 일을 Root, 디렉터리, Loadable 셋으로 나눠 에셋 하나씩 올리고 내립니다.
Addressables 의 원격 배포는 카탈로그가 이끈다
Addressables 의 원격 배포는 카탈로그를 바꿔 끼우는 일입니다. 기본 설정에서는 초기화 때 원격 카탈로그 해시를 비교해 새 카탈로그를 받고, 콘텐츠 업데이트 빌드 뒤에는 바뀐 번들만 새로 받습니다.
Addressables 는 번들 단위로 참조를 센다
Addressables 는 에셋마다 참조를 세지만 실제로 내리는 단위는 번들이라, 같은 번들의 다른 에셋이 살아 있으면 Release 한 에셋도 남습니다. Object.Instantiate 인스턴스는 세지 않고, InstantiateAsync 를 Destroy 만 하면 씬이 내려갈 때까지 번들이 열려 있습니다.
Addressables 의 그룹 설정이 번들 모양을 정한다
Addressables 의 그룹은 에디터 개념이고 런타임 단위는 그룹 설정이 만든 번들입니다. 묶음 방식에 따라 같은 20개 에셋이 번들 1개, 20개, 4개가 되고, 그룹끼리 공유하는 에셋은 Analyze 로 찾아 따로 빼야 한 벌이 됩니다.
Addressables 는 AssetBundle 위에 주소를 얹는다
Addressables 는 에셋에 주소를 붙여 그 주소로 로드하게 하고, 어느 번들에 있는지와 의존 번들을 여는 일을 대신합니다. 다만 에디터 기본 모드는 그룹 설정을 보지 않아서, 에디터에서만 되는 로드가 생깁니다.
AssetBundle 의 Unload 는 어느 쪽이든 사고가 난다
AssetBundle.Unload(false) 뒤 같은 번들을 다시 열면 옛 객체와 새 객체가 메모리에 같이 있고, 옛 것은 UnloadUnusedAssets 로만 치워집니다. Unload(true) 를 쓰는 중에 부르면 인스턴스의 머티리얼이 경고 없이 사라집니다.
AssetBundle 은 배정하지 않은 공용 에셋을 번들마다 복사한다
두 번들이 같은 머티리얼을 쓰는데 그 머티리얼을 어느 번들에도 넣지 않으면, AssetBundle 은 번들마다 따로 복사하고 둘을 열면 메모리에도 두 벌이 올라옵니다. 공용 에셋을 공용 번들에 명시하면 한 벌로 줄어듭니다.
AssetBundle 의 의존성은 직접 열어야 따라온다
AssetBundle 은 프리팹의 머티리얼이 다른 번들에 있으면 그 번들을 직접 열기 전까지 머티리얼이 비어 마젠타로 그립니다. 나중에 열면 끊긴 참조가 이어지지만, AssetBundleManifest 로 의존 번들을 먼저 여는 순서를 지킵니다.
AssetBundle 은 압축 방식에 따라 여는 비용이 다르다
LZMA 번들을 LoadFromFile 로 열면 번들 전체를 풀어 메모리에 둡니다. LZ4 와 무압축은 1ms 안에 열리고, LoadFromMemory 는 바이트 배열과 사본을 같이 들며, UnityWebRequest 에 버전을 주면 디스크 캐시를 써서 메모리가 크게 줄어듭니다.
AssetBundle 은 에셋을 Player 밖 파일로 꺼낸다
유니티는 Scene List 에서 참조를 따라 에셋을 Player 에 싣고, Resources 폴더는 참조가 없어도 통째로 싣습니다. AssetBundle 은 에셋 일부를 Player 밖 아카이브 파일로 빼서 따로 받고, 따로 열고, 따로 내리게 합니다.
관리 힙은 30MB 인데 게임은 2GB 를 먹는다
텍스처와 메시는 관리 힙에 거의 없습니다. GC 를 아무리 줄여도 여기는 안 줄어듭니다.
CoreCLR 이 와도 모바일은 그대로다
유니티가 Mono 를 걷어내고 세대 GC 를 들여옵니다. 그런데 IL2CPP 는 이 변화에서 빠져 있습니다.
증분 GC 는 일을 여러 프레임에 나눈다
기본으로 켜져 있고 프레임 튐을 줄여 줍니다. 다만 없애 주지는 않고, 공짜도 아닙니다.
보헴 GC 는 객체를 옮기지 않는다
보수적이고, 비압축이고, 비세대입니다. 이 세 단어가 프레임에서 어떻게 나타나는지 하나씩 봅니다.
유니티에서 프레임이 가끔 튀는 이유
평균은 멀쩡한데 몇 초에 한 번 걸립니다. 대개 GC 인데, 그 GC 가 지금 유니티에서 갈아 끼워지는 중입니다.