180bpm

Unity3D에서의 메모리 릭을 막기 위한 작업 방침. (작업중) 본문

Develop/Unity

Unity3D에서의 메모리 릭을 막기 위한 작업 방침. (작업중)

powdersnow 2017. 12. 22. 18:54

AssetBundle 사용시 발생하는 메모리 릭을 잡는데 오랜 시간이 걸렸다.

이에 따른 기록 노트.


에셋번들을 다뤄보지 않았는데 적용을 할 시점이라면

https://docs.unity3d.com/kr/current/Manual/AssetBundlesIntro.html

https://unity3d.com/kr/learn/tutorials/topics/scripting/assetbundles-and-assetbundle-manager
https://unity3d.com/kr/learn/tutorials/temas/best-practices/assetbundle-usage-patterns

등을 참고하기 바란다.


그리고 튜토리얼에서 제공하는 샘플 코드는 정말 샘플 코드일뿐이다.

실제로 적용할 플로우에 맞춰서 직접 테스트 코드를 작성하고 돌려봐야한다.

시뮬레이션과 실적용시의 메모리가 완전 다르다.



  1. 에셋번들 사용시 메모리 구조

    에셋번들(이하 번들) 이용하려면 다음과 같은 스텝을 거쳐야 한다


    파일에서 번들을 불러옴 -> 불러온 번들에서 에셋을 찾음 -> 에셋 인스턴스화


    불려온 번들은 profiler의 Other - SerializedFile에 구분 되고 인스턴트화 된 에셋은 Asset 카테고리로 들어간다.

    번들의 에셋을 직접 사용할수 없으니 메모리는 풀빌드에 비해 2배가 된다.


  2. 인스턴스를 소멸시켰는데 메모리가 예상만큼 내려가지 않아요!

    1번에서 설명한 번들때문이다. 번들도 해제를 시켜야 GC에 수집이 된다.

    우리는 로딩 시점에서 해제시켜준다.

  3. 메모리가 내려가지 않아요!

    이때부터 머리 아파지기 시작한다.

    메모리를 신경쓰는 시점은 개발 막바지쯔음이거나 라이브에서 이슈 생겼을때인데

    이때는 이미 코드가 방대해졌기 때문에 원인 파악이 굉장히 어렵다.

    onDestroy나 OnDestory처럼 MonoBehaviour에서 잡아놓은 예약어에 오타가 있으면..

    거기다가 다른 사람이 잘못된 방식으로 파일을 생성하고 있다면...

    작업하면서 별의별 문제때문에 안내려가고 있었다.

  4. 메모리가 안내려갈때의 상황
    https://unity3d.com/kr/learn/tutorials/temas/best-practices/assetbundle-usage-patterns

    1. "AB" 라는 이름의 번들을 로딩. 이후 M라는 에셋을 인스턴스화 시킴. M은 AB 번들의 P라는 에셋과 의존관계.
      이때 AB에셋은 SerializedFile에 잡히고 M 인스턴스는 Asset 카테고리에 임의의 P 주소값을 본다.
      기술

    2. AB를 내렸는데 M 인스턴스가 GC에 수집안되고 남아있다면 링크가 깨짐.
      이때 M 인스턴스는 계속 Aseet 카테고리에 있는 데이터를 쓰고 있다.
      GC에 수집 안되는 경우는 아래 "작업시 주의점"을 참조.
      기술

    3. AB에셋을 부르면 
      1번 상황처럼 AB에셋은 SerializedFile에 잡힘.
      M 인스턴스는 Asset 메모리 참조.
      기술

    4. 그리고 새로운 M 인스턴스를 생성하면 기존 인스턴스는 P 주소값, 새로운 인스턴스는 M 주소값. 이로 인해 메모리에 에셋이 중복으로 올라간다.
      기술

  5. 작업시 주의점

    1. GC를 믿지 마라.
      신입시절 사수가 늘 강조하던 말중 하나다.

    2. C#의 참조 기본값은 강한 참조다.
      예를 들어 싱글턴 패턴인 A 객체가 B 개체를 참조하고 있을경우, B 객체가 Scene에서 내려가도 메모리에는 여전히 남아있다.

    3. singleton 패턴/static 변수의 엄격한 사용제한
      이번 프로젝트에서는 UI간 이벤트 처리를 위해 싱글턴 패턴을 남발한 경향이 있다.
      static 변수가 많으면 코드간 물고 물리는게 많아서 문제점을 추적하기가 매우 어려워진다.
      (지금도 완벽하게 해결하지 못해서 우회해서 처리했다.)
      static 변수를 잡을 일이 있다면, 꼭 필요한지 생각해보자.

    4. data와 view를 분리하자.
      데이터와 UI, 컨트롤이 얽히는 순간 GC가 잡지 못한다.
      이거만 꼭 지켜도 메모리 프로파일링이 간결해진다.

    5. 처음부터 DeActive되었거나 initialization시점(Awake, OnEnable, Start) 에서 DeActive시킨 GameObject의 스크립트는 OnDestroy 호출 안됨
      4.x때도 한번 겪었던거같은데.. 또 같은 실수를 반복한다.
      외부에서 스크립트 초기화 코드를 호출시키는 형식을 쓰는데 이 때문에 발생한 문제다.
      OnDestroy 때 초기화 코드를 달아놨어도 정작 활성화 된적이 없는 오브젝트일경우 ondestory 호출이 되지 않는다. 주의.

    6. delegate 물리는순간 GC 수집안됨
      물리는순간 강한참조가 되어서 2번과 같은 상황이 된다.
      이벤트를 어떻게 처리할지 고민중.

  6. 어떻게 작업해야하나?

문서 작업하면서 생긴 의문점 노트

1.https://unity3d.com/kr/learn/tutorials/temas/best-practices/assetbundle-usage-patterns

문서에서는 AssetBundle.Unload(true)를 쓰고 false값은 되도록 쓰지 말라는데, 왜 에셋번들매니저에서는 false가 기본값인가.



Comments