|
|
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using MemoryProfilerWindow;
- using UnityEditor.MemoryProfiler;
- using UnityEngine;
-
- namespace MemoryProfilerWindow
- {
- class ShortestPathToRootFinder
- {
- private readonly CrawledMemorySnapshot _snapshot;
-
- public ShortestPathToRootFinder(CrawledMemorySnapshot snapshot)
- {
- _snapshot = snapshot;
- }
-
- public ThingInMemory[] FindFor(ThingInMemory thing)
- {
- var seen = new HashSet<ThingInMemory>();
- var queue = new Queue<List<ThingInMemory>>();
- queue.Enqueue(new List<ThingInMemory> { thing});
-
- while (queue.Any())
- {
- var pop = queue.Dequeue();
- var tip = pop.Last();
-
- string reason;
- if (IsRoot(tip, out reason))
- return pop.ToArray();
-
- foreach (var next in tip.referencedBy)
- {
- if (seen.Contains(next))
- continue;
- seen.Add(next);
- var dupe = new List<ThingInMemory>(pop) {next};
- queue.Enqueue(dupe);
- }
- }
- return null;
- }
-
- public bool IsRoot(ThingInMemory thing, out string reason)
- {
- reason = null;
- if (thing is StaticFields)
- {
- reason = "Static fields are global variables. Anything they reference will not be unloaded.";
- return true;
- }
- if (thing is ManagedObject)
- return false;
- if (thing is GCHandle)
- return false;
-
- var nativeObject = thing as NativeUnityEngineObject;
- if (nativeObject == null)
- throw new ArgumentException("Unknown type: " + thing.GetType());
- if (nativeObject.isManager)
- {
- reason = "this is an internal unity'manager' style object, which is a global object that will never be unloaded";
- return true;
- }
- if (nativeObject.isDontDestroyOnLoad)
- {
- reason = "DontDestroyOnLoad() was called on this object, so it will never be unloaded";
- return true;
- }
-
- if ((nativeObject.hideFlags & HideFlags.DontUnloadUnusedAsset) != 0)
- {
- reason = "the DontUnloadUnusedAsset hideflag is set on this object. Unity's builtin resources set this flag. Users can also set the flag themselves";
- return true;
- }
-
- if (nativeObject.isPersistent)
- return false;
-
- if (IsComponent(nativeObject.classID))
- {
- reason = "this is a component, living on a gameobject, that is either part of the loaded scene, or was generated by script. It will be unloaded on next scene load.";
- return true;
- }
-
- if (IsGameObject(nativeObject.classID))
- {
- reason = "this is a gameobject, that is either part of the loaded scene, or was generated by script. It will be unloaded on next scene load if nobody is referencing it";
- return true;
- }
-
- if (IsAssetBundle(nativeObject.classID))
- {
- reason = "this object is an assetbundle, which is never unloaded automatically, but only through an explicit .Unload() call.";
- return true;
- }
-
- reason = "This object is a root, but the memory profiler UI does not yet understand why";
- return true;
- }
-
- private bool IsGameObject(int classID)
- {
- return _snapshot.nativeTypes[classID].name == "GameObject";
- }
-
- private bool IsAssetBundle(int classID)
- {
- return _snapshot.nativeTypes[classID].name == "AssetBundle";
- }
-
- private bool IsComponent(int classID)
- {
- var packedNativeType = _snapshot.nativeTypes[classID];
-
- if (packedNativeType.name == "Component")
- return true;
-
- var baseClassID = packedNativeType.nativeBaseTypeArrayIndex;
-
- return baseClassID != -1 && IsComponent(baseClassID);
- }
- }
- }
|