1. 程式人生 > 其它 >Unity遊戲執行閃退問題定位與解決(標籤:Oops、crashed、System out of memory、UnityEngine.AssetBundle:LoadAsset_Internal)

Unity遊戲執行閃退問題定位與解決(標籤:Oops、crashed、System out of memory、UnityEngine.AssetBundle:LoadAsset_Internal)

技術標籤:Unity3DunityAssetBundleLoadFromFile閃退無效記憶體

文章目錄

一、閃退問題

最近Unity工程釋出PC平臺的包,運行遊戲,出現了一個閃退的問題,彈框如下。
在這裡插入圖片描述
根據提示,找到對應的crash日誌。
在這裡插入圖片描述
開啟output_log.txt,如下:

... ...
The file 'archive:/CAB-350107fab3529178780193de85391267/CAB-350107fab3529178780193de85391267' is corrupted! Remove it and launch unity again!
[Position out of bounds!]
 
(Filename:  Line: 220)

Mismatched serialization in the builtin class 'MonoScript'. (Read 41 bytes but expected 81 bytes)
 
(Filename:  Line: 1831)

DynamicHeapAllocator allocation probe 1 failed - Could not get memory for large allocation 4227858432.
DynamicHeapAllocator allocation probe 2 failed - Could not get memory for large allocation 4227858432.
DynamicHeapAllocator allocation probe 3 failed - Could not get memory for large allocation 4227858432.
DynamicHeapAllocator allocation probe 4 failed - Could not get memory for large allocation 4227858432.
DynamicHeapAllocator out of memory - Could not get memory for large allocation 4227858432!
Could not allocate memory: System out of memory!
Trying to allocate: 4227858432B with 16 alignment. MemoryLabel: BaseObject
Allocation happend at: Line:463 in 
Memory overview

[ ALLOC_DEFAULT ] used: 117172692B | peak: 0B | reserved: 128778716B 
[ ALLOC_TEMP_JOB ] used: 0B | peak: 0B | reserved: 2097152B 
[ ALLOC_GFX ] used: 39689080B | peak: 0B | reserved: 44995820B 
[ ALLOC_CACHEOBJECTS ] used: 1044176B | peak: 0B | reserved: 10485760B 
[ ALLOC_TYPETREE ] used: 1242064B | peak: 0B | reserved: 4194304B 
[ ALLOC_PROFILER ] used: 0B | peak: 0B | reserved: 0B 
[ ALLOC_TEMP_THREAD ] used: 34816B | peak: 0B | reserved: 3244032B 
Could not allocate memory: System out of memory!
Trying to allocate: 4227858432B with 16 alignment. MemoryLabel: BaseObject
Allocation happend at: Line:463 in 
Memory overview

[ ALLOC_DEFAULT ] used: 117172692B | peak: 0B | reserved: 128778716B 
[ ALLOC_TEMP_JOB ] used: 0B | peak: 0B | reserved: 2097152B 
[ ALLOC_GFX ] used: 39689080B | peak: 0B | reserved: 44995820B 
[ ALLOC_CACHEOBJECTS ] used: 1044176B | peak: 0B | reserved: 10485760B 
[ ALLOC_TYPETREE ] used: 1242064B | peak: 0B | reserved: 4194304B 
[ ALLOC_PROFILER ] used: 0B | peak: 0B | reserved: 0B 
[ ALLOC_TEMP_THREAD ] used: 34816B | peak: 0B | reserved: 3244032B 

 
(Filename:  Line: 999)

Crash!!!
... ...
========== OUTPUTING STACK TRACE ==================

  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9DCD0)
0x00F9DCD0 (SFish) 
  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9F88C)
0x00F9F88C (SFish) 
  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9FFAC)
0x00F9FFAC (SFish) 
0x0121A644 (SFish) physx::shdfnd::Foundation::getTempAllocFreeTable
0x012157E3 (SFish) physx::shdfnd::Foundation::getTempAllocFreeTable
0x0121666A (SFish) physx::shdfnd::Foundation::getTempAllocFreeTable
  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9AFDF)
0x00F9AFDF (SFish) 
  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9A553)
0x00F9A553 (SFish) 
  ERROR: SymGetSymFromAddr64, GetLastError: '試圖訪問無效的地址。

' (Address: 00F9A7A9)
0x00F9A7A9 (SFish) 
0x0123D395 (SFish) physx::shdfnd::Foundation::getTempAllocFreeTable
0x05F9875A (Mono JIT Code) (wrapper managed-to-native) UnityEngine.AssetBundle:LoadAsset_Internal (string,System.Type)
0x05F9867D (Mono JIT Code) UnityEngine.AssetBundle:LoadAsset (string,System.Type)
0x05F98571 (Mono JIT Code) QRes.AssetBundleMgr:LoadAsset (string,string,bool)
0x05F9834E (Mono JIT Code) QRes.AssetBundleMgr:LoadExternAsset (string)
0x05F9817B (Mono JIT Code) QRes.AssetBundleMgr:LoadAssetFromAssetBundle (string)
0x17746A0F (Mono JIT Code) RbResDestory:Instantiate<object> (string)
0x17746908 (Mono JIT Code) RbResDestory:InstantiateGameObject (int)
0x15C4BB46 (Mono JIT Code) CR_NeHeRunner:Instead (intptr)
0x177A95A5 (Mono JIT Code) (wrapper native-to-managed) CR_NeHeRunner:Instead (intptr)
0x6DF8334E (tolua) lua_lessthan
  ERROR: SymGetSymFromAddr64, GetLastError: '找不到指定的模組。

' (Address: 1F293288)
  ERROR: SymGetModuleInfo64, GetLastError: '動態連結庫(DLL)初始化例程失敗。

' (Address: 1F293288)
0x1F293288 ((<unknown>)) 

========== END OF STACKTRACE ===========

**** Crash! ****

二、問題定位與解決

1、結論

根據日誌,得出結論:是在呼叫AssetBundleLoadAsset介面的時候閃退的,訪問了一個無效的記憶體地址。
但,為什麼呢?

2、問題分析

遊戲中做了資源下載的功能:把資源打成AssetBundle,放在雲端,客戶端運行遊戲時從雲端下載AssetBundle包到本地,然後從本地中載入資源。

這個流程會存在一個什麼問題呢?
假設我們有個資源包:test.ab,我們把它放到雲端,客戶端下載到了本地並載入了這個AssetBundle;接著我們工程中修改了資源,重新打了test.ab放到雲端,遊戲客戶端在不重啟的情況下又觸發了test.ab的下載,那麼本地磁碟中的test.ab

就會被覆蓋,這就導致了之前載入到記憶體中的AssetBundle物件與磁碟的AssetBundle檔案不對應了,如果此時呼叫LoadAsset介面就會出現無效記憶體訪問,導致閃退。

這又引出了另一個疑問,我們載入AssetBundle的時候,程式碼如下:

AssetBundle ab = AssetBundle.LoadFromFile(ab_path);

我們已經通過AssetBundle.LoadFromFile載入了AssetBundle到記憶體中,為什麼此時刪除或者覆蓋磁碟中的AssetBundle會影響記憶體中的AssetBundle呢?
事實上,AssetBundle.LoadFromFile

只是載入了資源清單檔案,真正的資源並沒有載入進記憶體中,只有當我們呼叫LoadAsset接口才會真正把資源從磁碟中讀取到記憶體中,並且只讀取對應的Asset;如果想要讀取AssetBundle中所有的Asset,可以使用LoadAllAssetsLoadAllAssetsAsync載入所有Asset
當我們使用LoadAsset讀取了某個資源到記憶體中後,即使磁碟中的AssetBundle刪除或者被覆蓋成其他檔案,都不會再影響記憶體中再次LoadAsset這個資源了,也就是說,記憶體中的AssetBundle物件自身是會有一層快取的,第一次呼叫LoadAsset時是從磁碟中的AssetBundle檔案中讀取資源,再次呼叫LoadAsset相同資源是,直接從記憶體中的AssetBundle物件快取中讀取了。

3、解決辦法

根據分析,解決辦法就是首次載入AssetBundle物件後就執行LoadAllAssetsAsync載入所有Asset,即可避免因為資源覆蓋下載導致LoadAsset出現無效記憶體訪問。