Unity的Profiler可以看出来内存,但是遇到ManagedStaticReferences()估计大家都跪了。因为这个资源被静态引用了,然后我们并不知道它被哪里静态引用了。
我想了个办法,可以通过反射来查询它。先讲一下原理
1.反射dll
2.遍历所有的.cs 取出所有带static的属性。
3.对static下的所有属性以及子属性进行深度遍历,最终即可得出来被静态引用的资源。
如下代码所示,运行环境unity5.2.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Reflection; using System; using System.Text; public partial class ReportAsset { static HashSet<object> s_Check; [MenuItem("Tools/Report/脚本Static引用")] static void StaticRef() { s_Check = new HashSet<object>();//避免被循环引用导致递归 //静态引用 LoadAssembly("Assembly-CSharp-firstpass"); LoadAssembly("Assembly-CSharp"); } static void LoadAssembly(string name) { Assembly assembly = null; try { assembly = Assembly.Load(name); } catch (Exception ex) { Debug.LogWarning(ex.Message); } finally { if (assembly != null) { foreach (Type type in assembly.GetTypes()) { try { HashSet<string> assetPaths = new HashSet<string>(); FieldInfo[] listFieldInfo = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); foreach (FieldInfo fieldInfo in listFieldInfo) { if (!fieldInfo.FieldType.IsValueType) { SearchProperties(fieldInfo.GetValue(null), assetPaths); } } if (assetPaths.Count > 0) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}.cs\n", type.ToString()); foreach (string path in assetPaths) { sb.AppendFormat("\t{0}\n", path); } Debug.LogError(sb.ToString()); } } catch (Exception ex) { Debug.LogWarning(ex.Message); } } } } } static HashSet<string> SearchProperties(object obj, HashSet<string> assetPaths) { if (obj != null && s_Check.Add(obj)) { if (obj is UnityEngine.Object) { UnityEngine.Object[] depen = EditorUtility.CollectDependencies(new UnityEngine.Object[] { obj as UnityEngine.Object }); foreach (var item in depen) { string assetPath = AssetDatabase.GetAssetPath(item); if (!string.IsNullOrEmpty(assetPath)) { assetPaths.Add(assetPath); } } } else if (obj is IEnumerable) { foreach (object child in (obj as IEnumerable)) { SearchProperties(child, assetPaths); } } else if (obj is System.Object) { if (!obj.GetType().IsValueType) { FieldInfo[] fieldInfos = obj.GetType().GetFields(); foreach (FieldInfo fieldInfo in fieldInfos) { object o = fieldInfo.GetValue(obj); if (o != obj) { SearchProperties(fieldInfo.GetValue(obj), assetPaths); } } } } } return assetPaths; } } |
问题:
1.这个脚本查不出lua里的静态引用。lua可以遍历G里的所有table表,和上面执行类型的操作就可以查出来。
2.如果想定向查询,比如专门查某一个资源在内存中哪里被引用,也和上面算法差不多改改就可以。
3.Unity在Editor下的Profiler其实是非常不准确的,因为自己在Project视图中选择一些资源unity也会记录在Profiler中,并且没办法完全清除。如果想测试内存,可以打一个pc包在连上Profiler在看即可。
最后,如果大家有更好的方法,欢迎再下面给我留言,我们一起讨论~
- 本文固定链接: https://www.xuanyusong.com/archives/4390
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
代码递归退出条件少了一条,环形引用的话,会导致堆栈溢出,untiy直接crash了
今天测试也发现这个问题了,已经补充进代码中。
冒个泡
我这边项目 泄露的还是查不出来……
有些不一定是被static引用、环形引用 也可能这样。