前言html
最近進行項目性能優化的時候發現的問題。性能優化
問題編輯器
從大廳進到單局的過程當中,會通過選擇英雄和加載兩個流程,這兩個流程對應的UI界面都會有一張幾mb左右的貼圖做爲背景,在進入單局遊戲後這兩個UI已經銷燬了。工具
以後調用下對應的Resources的相關接口,按理來講圖集貼圖就應該釋放掉了。性能
Resources.UnloadUnusedAssets()
然而並無,用Profiler檢查了,發現被父級的層級Layer裏的GraphicRaycaster引用了(好比下圖的英雄界面背景)。優化
基本上每次進單局都有10mb左右的內存沒釋放掉spa
問題排查3d
暴力重建htm
項目主程給個人建議把這層Layer直接Destroy掉重建,這樣確實能解決問題,可是有可能摧毀的時候上面還有其餘UI,致使UI註冊信息還在相關的gameObject卻沒了,訪問UI的時候會拋出NullReference的異常,因此這個方法太簡單暴力了,很差。blog
查閱源碼
嘗試閱讀了GraphicRaycaster的源碼,發現它內部維護了兩個Graphic的列表
這兩個列表只有在發起一次新的射線的時候纔會清空(進單局後選人和加載的Layer子節點下不會有新的UI接受觸摸射線了),因而猜想多是List一直沒清空致使的圖集沒法釋放。
emmmmm,官方還打上了反序列化的標籤,這樣編輯器的Debug模式下也沒法查看這兩個List的數據了。
沒辦法,拷貝了整個代碼到一個新類TestGraphicRaycaster,把這兩個標籤替換爲SerializeField,而後把相關Layer上的GraphicRaycaster用這個腳本替換了下,而後運行遊戲。
果真跟猜想的同樣,是m_RaycastResults這個列表保存的Graphic沒有Clear掉,這樣Graphic沒法被GC回收,進而致使Graphic持有的圖集也沒法被釋放掉。
找到問題了,那就好解決了。
解決方法
直接清理列表
建立一個新類,把GraphicRaycaster的源碼拷貝到新類當中,而後添加一個清理的接口
public void ClearRaycastResults() { if(m_RaycastResults != null) { m_RaycastResults.Clear(); } }
使用反射清理列表
若是你沒有源碼或者不想修改源碼,那麼用反射獲取對應的列表清理也是能夠的。
using System.Reflection; //放在你的工具類裏 public static void ClearRaycastResults(GraphicRaycaster gRaycaster) { if(gRaycaster != null) { var fieldInfo = gRaycaster.GetType().GetField("m_RaycastResults", BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance); if (fieldInfo != null) { List<Graphic> list = fieldInfo.GetValue(gRaycaster) as List<Graphic>; if(list != null) { list.Clear(); } } } }
而後在釋放資源前調用下上述方法清理掉GraphicRaycaster的列表就好了
參考資料
相關信息能夠參考我在UnityAnswer上發佈的這個問題