WebGL 성능 고려사항
WebGL: 브라우저 스크립트와 상호작용

WebGL의 메모리

Unity WebGL에서 메모리는 실행가능한 콘텐츠의 복잡성을 제한하는 억제 요소가 될 수 있습니다. 이 페이지에서는 WebGL의 메모리 사용 방법에 대해 설명합니다.

WebGL 콘텐츠는 브라우저 안에서 실행되므로 브라우저에서 메모리를 브라우저의 메모리 공간 안에 할당해야 합니다. 사용 가능한 메모리의 양은 사용하는 브라우저, OS 및 기기에 따라 매우 다를 수 있습니다. 결정 요인은 브라우저가 32비트 프로세스인지 64비트 프로세스인지, 브라우저가 각 탭마다 별도의 프로세스를 사용하는지 아니면 콘텐츠가 모든 열린 탭과 함께 메모리 공간을 공유하는지, 그리고 브라우저의 JavaScript 엔진이 코드를 파싱하는 데 메모리가 얼마나 필요한지 등입니다.

브라우저에서 Unity WebGL 콘텐츠에 상당한 양의 메모리를 할당해야 하는 다음과 같은 여러 부분이 있습니다.

Unity 힙

Unity에서 모든 상태와 관리 및 네이티브 오브젝트와 현재 로드된 에셋 및 씬을 저장하는 데 사용하는 메모리입니다. 다른 플랫폼에서 Unity 플레이어에 사용되는 메모리와 유사합니다. Unity WebGL 플레이어 설정에서 크기를 설정할 수 있지만, 생성된 html 파일에 작성된 TOTAL_MEMORY 값을 편집하여 반복 속도를 높일 수도 있습니다. Unity 프로파일러를 사용하여 이 메모리의 콘텐츠를 프로파일링하고 샘플링할 수 있습니다. 이 메모리는 JavaScript 코드에서 바이트의 TypedArray로 생성되며, 브라우저에서 이 크기의 연속 메모리 블록을 할당할 수 있어야 합니다. 이 공간은 (메모리가 조각난 경우에도 브라우저에서 할당할 수 있도록)최대한 작으면서도 콘텐츠의 모든 씬을 플레이하는 데 필요한 모든 데이터를 저장할 수 있을 만큼 커야 합니다.

에셋 데이터

Unity WebGL 빌드를 만들면 Unity가 콘텐츠에 필요한 모든 씬과 에셋이 포함된 .data 파일을 작성합니다. WebGL에는 실제 파일 시스템이 없으므로 이 파일은 콘텐츠를 시작할 수 있기 전에 다운로드되고 압축을 푼 데이터는 콘텐츠가 실행되는 동안 브라우저 메모리의 연속적인 블록에 계속 보관됩니다. 따라서 다운로드 시간과 메모리 사용량을 모두 낮게 유지하려면 이 데이터를 최대한 작게 유지하려고 해야 합니다. 에셋의 빌드 크기를 최적화하는 방법에 대한 내용은 파일 크기 축소에 대한 문서 페이지를 참조하십시오.

에셋 데이터를 에셋 번들에 패킹하여 로드 시간과 에셋에 사용되는 메모리 양을 줄일 수도 있습니다. 그러면 에셋을 다운로드해야 하는 시기를 다음대로 관리하고 더 이상 필요하지 않은 에셋을 언로드할 수 있으므로 에셋에 사용되는 메모리를 사용 가능한 메모리로 전환할 수 있습니다. 에셋 번들은 Unity 힙에 직접 로드되므로 (브라우저의 IndexDB를 통해 지원되는 메모리 맵 버추얼 파일 시스템을 사용하는 WWW.LoadFromCacheOrDownload 를 사용하여 에셋 번들 캐싱을 사용하지 않는 한) 브라우저에서 메모리를 추가로 할당하지 않습니다.

코드 파싱에 필요한 메모리

브라우저의 JavaScript 엔진에 필요한 메모리와 관련된 문제도 있습니다. Unity는 생성된 JavaScript 코드의 라인이 수백만 개 포함된 매우 큰 파일을 내보내는데, 이 파일의 크기는 브라우저에서 JavaScript 코드를 일반적인 용도로 사용하는 경우에 생성되는 파일보다 훨씬 더 큽니다. 일부 JavaScript 엔진은 이 코드를 파싱하고 최적화하기 위해 비교적 큰 데이터 구조를 할당하므로, 콘텐츠를 로드할 때 경우에 따라 메모리 사용량이 수 메가바이트까지 급증할 수 있습니다. WebAssembly 같은 미래 기술로 결국에는 이 문제를 해결할 수 있을 것으로 예상되지만, 그 때까지는 내보내는 코드의 크기를 최대한 작게 유지하는 방법이 최선입니다. 이렇게 하는 방법에 대한 자세한 내용은 여기서 배포 크기에 대한 설명을 참조하십시오.

메모리 문제 해결

Unity WebGL 빌드에서 메모리 관련 오류가 표시되는 경우 브라우저의 메모리 할당 실패가 원인인지 Unity WebGL 런타임이 Unity 힙의 미리 할당된 블록 안에 빈 메모리 블록 할당하는 데 실패한 것이 원인인지 알아야 합니다. 브라우저의 메모리 할당 실패가 원인인 경우, (예를 들어, Unity 힙의 크기를 축소하는 등) 위의 메모리 영역 중 하나 이상에 사용되는 크기를 줄여보면 도움이 될 수 있습니다. 반면에 Unity 런타임이 Unity 힙 안에 블록을 할당하는 데 실패하는 경우 크기를 늘려야 할 수 있습니다.

Unity는 오류 메시지를 해석하여 어느 경우에 해당하는지 확인하고 해결 방법을 제안하기 위해 노력합니다. 하지만 브라우저마다 보고되는 메시지가 다를 수 있기 때문에 이렇게 하기가 쉽지 않을 수 있고 일부 메시지는 해석되지 않을 수 있습니다. 브라우저에서 일반적인 “메모리 부족” 오류가 표시되면 대부분은 브라우저의 메모리 부족 문제일 수 있습니다. 이 경우 더 작은 Unity 힙을 사용하는 것이 좋습니다. 때로는 Unity 콘텐츠를 로드할 때 사람이 해석할 수 있는 오류 메시지가 표시되지 않고 브라우저가 갑자기 크래시하는 경우도 발생할 수 있습니다. 다양한 원인이 있을 수 있지만, JavaScript 엔진이 생성된 코드를 파싱하고 최적화하는 데 메모리를 너무 많이 필요로 하기 때문인 경우가 많습니다.

광범위 할당 Http 헤더

서버에서 콘텐츠에 대한 광범위 할당 http 헤더를 내보낼 수 있습니다. 헤더는 지원되는 브라우저(현재 Firefox만 지원됨)에 필요한 메모리에 대해 알리므로, 브라우저에서는 이 정보를 통해 조각나지 않은 메모리 공간을 사용하여 새 프로세스를 소폰하거나 대용량 할당에 성공하도록 보장하기 위한 다른 하우스키핑 작업을 수행할 수 있습니다. 이렇게 하면 특히 32비트 브라우저에서 Unity 힙을 할당하려고 할 때, 브라우저 메모리가 부족해지는 문제를 해결할 수 있습니다.

가비지 컬렉션(Garbage Collection) 고려사항

Unity에서 관리 오브젝트를 할당하는 경우, 오브젝트가 더 이상 사용되지 않으면 가비지 컬렉션을 수행해야 합니다. 자세한 내용은 자동 메모리 관리 문서를 참조하십시오. WebGL에서도 마찬가지입니다. 가비지 컬렉션을 통해 수거된 관리 메모리는 Unity 힙 안에 할당됩니다.

하지만 WebGL에서는 가비지 컬렉션(GC)이 수행될 수 있는 시점에 관한 한 가지 차이점이 있습니다. 가비지 컬렉션을 수행하려면 GC에서 일반적으로 모든 실행 중인 스레드를 일시 중지하고 스레드의 스택 및 레지스터를 검사하고 로드된 오브젝트 레퍼런스가 있는지 확인해야 합니다. 현재 JavaScript에서는 이렇게 할 수 없습니다. 그렇기 때문에 GC는 스택이 비어있는 것으로 알려진 상황(현재 모든 프레임 후 한 번)에만 WebGL에서 실행됩니다. 관리 메모리를 보수적으로 처리하고 각 프레임 안에 GC 할당이 비교적 적은 대부분의 콘텐츠에서는 문제가 되지 않고, Unity 프로파일러를 사용하여 디버그할 수 있습니다.

하지만 이 코드를 따르면 모든 중간 문자열 오브젝트에 사용되는 메모리를 비우기 위해 루프 반복 사이에 GC를 실행할 기회가 없으므로 WebGL에서 실행하는 데 실패합니다. 따라서 결국에는 Unity 힙에서 메모리가 부족해집니다.

string hugeString = "";

for (int i = 0; i < 100000; i++)
{
    hugeString += "foo";
}

추가 정보


2018–08–23 편집 리뷰 없이 페이지 수정됨

WebGL 성능 고려사항
WebGL: 브라우저 스크립트와 상호작용