Unity_Addressable_Memory management(內(nèi)存管理)
The Addressables system manages the memory used to load assets and bundles by keeping a reference count of every item it loads.
譯:Addressables系統(tǒng)通過(guò)跟蹤每個(gè)加載的項(xiàng)的引用計(jì)數(shù)來(lái)管理用于加載資產(chǎn)和Bundle的內(nèi)存。
When an Addressable is loaded, the system increments the reference count; when the asset is released, the system decrements the reference count. When the reference count of an Addressable returns to zero, it is eligible to be unloaded. When you explicitly load an Addressable asset, you must also release the asset when you are done with it.
譯:當(dāng)加載Addressable時(shí),系統(tǒng)會(huì)增加引用計(jì)數(shù);當(dāng)釋放資產(chǎn)時(shí),系統(tǒng)會(huì)減少引用計(jì)數(shù)。當(dāng)Addressable的引用計(jì)數(shù)返回到零時(shí),它就可以被卸載。當(dāng)你顯式加載Addressable資產(chǎn)時(shí),你必須在使用完畢后釋放資產(chǎn)
The basic rule of thumb to avoid "memory leaks" (assets that remain in memory after they are no longer needed) is to mirror every call to a load function with a call to a release function. You can release an asset with a reference to the asset instance itself or with the result handle returned by the original load operation.
譯:避免“內(nèi)存泄漏”的基本原則是,每個(gè)加載函數(shù)的調(diào)用都要與一個(gè)釋放函數(shù)的調(diào)用相對(duì)應(yīng)。您可以使用資產(chǎn)實(shí)例本身的引用或原始加載操作返回的結(jié)果句柄來(lái)釋放資產(chǎn)。
Note, however, that released assets are not necessarily unloaded from memory immediately. The memory used by an asset is not freed until the AssetBundle it belongs to is also unloaded. (Released assets can also be unloaded by calling?Resources.UnloadUnusedAssets, but that tends to be a slow operation which can cause frame rate hitches.)
譯:然而,請(qǐng)注意,釋放的資產(chǎn)不一定會(huì)立即從內(nèi)存中卸載。一個(gè)資產(chǎn)所使用的內(nèi)存直到它所屬的AssetBundle也被卸載才會(huì)被釋放。(釋放的資產(chǎn)也可以通過(guò)調(diào)用Resources.UnloadUnusedAssets來(lái)卸載,但這往往是一個(gè)緩慢的操作,會(huì)導(dǎo)致幀率的停頓。)
AssetBundles have their own reference count (the system treats them like Addressables with the assets they contain as dependencies). When you load an asset from a bundle, the bundle's reference count increases and when you release the asset, the bundle reference count decreases. When a bundle's reference count returns to zero, that means none of the assets it contains are still in use and the bundle and all the assets it contains are unloaded from memory.
譯:AssetBundle有自己的引用計(jì)數(shù)(系統(tǒng)將它們視為包含依賴(lài)項(xiàng)的Addressables)。當(dāng)您從Bundle中加載資產(chǎn)時(shí),Bundle的引用計(jì)數(shù)將增加,當(dāng)您釋放資產(chǎn)時(shí),Bundle的引用計(jì)數(shù)將減少。當(dāng)Bundle的引用計(jì)數(shù)返回到零時(shí),這意味著它包含的所有資產(chǎn)都不再使用,該Bundle及其包含的所有資產(chǎn)都會(huì)從內(nèi)存中卸載。
Use the?Event Viewer?to monitor your runtime memory management. The viewer shows when assets and their dependencies are loaded and unloaded.
譯:使用事件查看器來(lái)監(jiān)視您的運(yùn)行時(shí)內(nèi)存管理。該查看器顯示資產(chǎn)及其依賴(lài)項(xiàng)何時(shí)被加載和卸載。
Understanding when memory is cleared
An asset no longer being referenced (indicated by the end of a blue section in the?Event Viewer) does not necessarily mean that asset was unloaded. A common applicable scenario involves multiple assets in an AssetBundle. For example:
譯:資產(chǎn)不再被引用(在事件查看器中的藍(lán)色部分結(jié)束)并不一定意味著該資產(chǎn)已被卸載。常見(jiàn)的應(yīng)用場(chǎng)景包括AssetBundle中的多個(gè)資產(chǎn)。例如:
You have three Assets (
tree
,?tank
, and?cow
) in an AssetBundle (stuff
).譯:您在AssetBundle(stuff)中有三個(gè)資產(chǎn)(tree,tank和cow)。When?
tree
?loads, the profiler displays a single ref-count for?tree
, and one for?stuff
.譯:當(dāng)tree加載時(shí),Profiler顯示tree的單個(gè)引用計(jì)數(shù)和stuff的一個(gè)引用計(jì)數(shù)。Later, when?
tank
?loads, the profiler displays a single ref-count for both?tree
?and?tank
, and two ref-counts for the?stuff
?AssetBundle.譯:稍后,當(dāng)tank加載時(shí),Profiler顯示tree和tank的單個(gè)引用計(jì)數(shù)以及stuff AssetBundle的兩個(gè)引用計(jì)數(shù)。?If you release?
tree
, it's ref-count becomes zero, and the blue bar goes away.譯:如果您釋放tree,則其引用計(jì)數(shù)變?yōu)榱悖{(lán)色條消失。
In this example, the?tree
?asset is not actually unloaded at this point. You can load an AssetBundle, or its partial contents, but you cannot partially unload an AssetBundle. No asset in stuff unloads until the AssetBundle itself is completely unloaded. The exception to this rule is the engine interface?Resources.UnloadUnusedAssets. Executing this method in the above scenario causes?tree
?to unload. Because the Addressables system cannot be aware of these events, the profiler graph only reflects the Addressables ref-counts (not exactly what memory holds). Note that if you choose to use?Resources.UnloadUnusedAssets, it is a very slow operation, and should only be called on a screen that won't show any hitches (such as a loading screen).
譯:在這個(gè)例子中,tree資產(chǎn)實(shí)際上在這個(gè)時(shí)候并沒(méi)有被卸載。你可以加載AssetBundle或它的部分內(nèi)容,但你不能部分卸載AssetBundle。直到AssetBundle本身完全卸載,stuff中的任何資產(chǎn)都不會(huì)卸載。這個(gè)規(guī)則的例外是引擎接口Resources.UnloadUnusedAssets。在上述情況下執(zhí)行此方法會(huì)導(dǎo)致tree卸載。因?yàn)锳ddressables系統(tǒng)不能意識(shí)到這些事件,Profiler圖表只反映Addressables引用計(jì)數(shù)(并不完全反映內(nèi)存的實(shí)際情況)。請(qǐng)注意,如果您選擇使用Resources.UnloadUnusedAssets,它是一個(gè)非常緩慢的操作,只應(yīng)該在不會(huì)顯示任何停頓的屏幕上調(diào)用(例如加載屏幕)
Avoiding asset churn
Asset churn is a problem that can arise if you release an object that happens to be the last item in an AssetBundle and then immediately reload either that asset or another asset in the bundle.
譯:如果您釋放一個(gè)恰好是AssetBundle中最后一項(xiàng)的對(duì)象,然后立即重新加載該資產(chǎn)或Bundle中的另一個(gè)資產(chǎn),則可能會(huì)出現(xiàn)資產(chǎn)變動(dòng)的問(wèn)題。
For example, say you have two materials,?boat
?and?plane
?that share a texture,?cammo
, which has been pulled into its own AssetBundle. Level 1 uses?boat
?and level 2 uses?plane
. As you exit level 1 you release?boat
, and immediately load?plane
. When you release?boat
, Addressables unloads texture?cammo
. Then, when you load?plane
, Addressables immediately reloads?cammo
.
譯:例如,假設(shè)您有兩個(gè)材質(zhì),boat和plane,共享一個(gè)紋理cammo,該紋理已被拉入其自己的AssetBundle中。Level1使用boat,Level2使用plane。當(dāng)您退出Level1并釋放boat時(shí),Addressables卸載紋理cammo。然后,當(dāng)您加載plane時(shí),Addressables立即重新加載cammo。
You can use the?Event Viewer?to help detect asset churn by monitoring asset loading and unloading.
譯:您可以使用事件查看器來(lái)通過(guò)監(jiān)控資產(chǎn)的加載和卸載來(lái)幫助檢測(cè)資產(chǎn)變動(dòng)。
AssetBundle memory overhead
When you load an AssetBundle, Unity allocates memory to store the bundle's internal data, which is in addition to the memory used for the assets it contains. The main types of internal data for a loaded AssetBundle include:
譯:當(dāng)您加載AssetBundle時(shí),Unity會(huì)分配內(nèi)存來(lái)存儲(chǔ)該Bundle的內(nèi)部數(shù)據(jù),這是除了用于包含的資產(chǎn)的內(nèi)存之外的另一部分內(nèi)存。已加載的AssetBundle的主要內(nèi)部數(shù)據(jù)類(lèi)型包括:
Loading cache: Stores recently accessed pages of an AssetBundle file.?Use?AssetBundle.memoryBudgetKB?to control its size.譯:加載緩存:存儲(chǔ)AssetBundle文件的最近訪問(wèn)頁(yè)面。使用AssetBundle.memoryBudgetKB來(lái)控制其大小。
TypeTrees: Defines the serialized layout of your objects.譯:TypeTrees:定義對(duì)象的序列化布局
Table of contents: Lists the assets in a bundle.譯:目錄表:列出Bundle中的資產(chǎn)。
Preload table: Lists the dependencies of each asset.譯:預(yù)加載表:列出每個(gè)資產(chǎn)的依賴(lài)項(xiàng)。
When you organize your Addressable groups and AssetBundles, you typically must make trade-offs between the size and the number of AssetBundles you create and load. On the one hand, fewer, larger bundles can minimize the total memory usage of your AssetBundles. On the other hand, using a larger number of small bundles can minimize the peak memory usage because you can unload assets and AssetBundles more easily.
譯:當(dāng)您組織Addressable組和AssetBundle時(shí),通常必須在創(chuàng)建和加載的AssetBundle的大小和數(shù)量之間進(jìn)行權(quán)衡。一方面,較少但更大的Bundle可以最小化AssetBundle的總內(nèi)存使用量。另一方面,使用更多但更小的Bundle可以最小化峰值內(nèi)存使用量,因?yàn)槟梢愿p松地卸載資產(chǎn)和AssetBundle。
While the size of an AssetBundle on disk is not the same as its size at runtime, you can use the disk size as an approximate guide to the memory overhead of the AssetBundles in a build. You can get bundle size and other information you can use to help analyze your AssetBundles from the?Build Layout Report.
譯:雖然AssetBundle在磁盤(pán)上的大小與其運(yùn)行時(shí)大小不同,但您可以使用磁盤(pán)大小作為構(gòu)建中AssetBundle的內(nèi)存開(kāi)銷(xiāo)的近似指南。您可以從Build Layout Report獲取Bundle大小和其他信息,以幫助分析AssetBundle。
The following sections discuss the internal data used by AssetBundles and how you can minimize the amount of memory they require, where possible.
譯:以下部分討論了AssetBundle使用的內(nèi)部數(shù)據(jù)以及在可能的情況下如何最小化它們所需的內(nèi)存量。
TypeTrees
A TypeTree describes the field layout of one of your data types.
譯:TypeTree描述了一個(gè)數(shù)據(jù)類(lèi)型的字段布局。
Each serialized file in an AssetBundle contains a TypeTree for each object type within the file. The TypeTree information allows you to load objects that are deserialized slightly differently from the way they were serialized. TypeTree information is not shared between AssetBundles; each bundle has a complete set of TypeTrees for the objects it contains.
譯:AssetBundle中的每個(gè)序列化文件都包含文件中每個(gè)對(duì)象類(lèi)型的TypeTree。TypeTree信息允許您加載反序列化的對(duì)象與序列化時(shí)略有不同。TypeTree信息不在AssetBundle之間共享;每個(gè)Bundle都有一組包含其中所包含的對(duì)象的完整TypeTrees。
All the TypeTrees are loaded when the AssetBundle is loaded and held in memory for the lifetime of the AssetBundle. The memory overhead associated with TypeTrees is proportional to the number of unique types in the serialized file and the complexity of those types.
譯:當(dāng)加載AssetBundle時(shí),所有TypeTrees都會(huì)被加載并保存在內(nèi)存中,直到AssetBundle的生命周期結(jié)束。與TypeTrees相關(guān)的內(nèi)存開(kāi)銷(xiāo)與序列化文件中唯一類(lèi)型的數(shù)量和這些類(lèi)型的復(fù)雜性成正比
You can reduce the memory requirements of AssetBundle TypeTrees in the following ways:
譯:您可以通過(guò)以下方式減少AssetBundle TypeTrees的內(nèi)存要求
Keep assets of the same types together in the same bundles.譯:在同一Bundle中將相同類(lèi)型的資產(chǎn)放在一起。
Turn off TypeTrees -- turning off TypeTrees makes your AssetBundles smaller by excluding this information from a bundle. However, without TypeTree information, you may encounter serialization errors or undefined behavior when loading older bundles with a newer version of Unity or after making even small script changes in your project.譯:關(guān)閉TypeTrees - 關(guān)閉TypeTrees會(huì)使AssetBundle更小,因?yàn)樗鼘⒋诵畔腂undle中排除。但是,沒(méi)有TypeTree信息,當(dāng)使用較新版本的Unity或在項(xiàng)目中進(jìn)行即使是小的腳本更改后,加載舊Bundle時(shí)可能會(huì)遇到序列化錯(cuò)誤或未定義行為。
Prefer simpler data types to reduce TypeTree complexity.譯:更喜歡簡(jiǎn)單的數(shù)據(jù)類(lèi)型以減少TypeTree的復(fù)雜性。
You can test the impact that TypeTrees have on the size of your AssetBundles by building them with and without TypeTrees disabled and comparing the sizes.
譯:您可以通過(guò)啟用和禁用TypeTrees來(lái)測(cè)試TypeTrees對(duì)AssetBundle大小的影響,并比較它們的大小。
Use?BuildAssetBundleOptions.DisableWriteTypeTree?to disable TypeTrees in your AssetBundles. Note that not all platforms support TypeTrees and some platforms require TypeTrees (and ignore this setting).
譯:使用BuildAssetBundleOptions.DisableWriteTypeTree在AssetBundle中禁用TypeTrees。請(qǐng)注意,并非所有平臺(tái)都支持TypeTrees,有些平臺(tái)需要TypeTrees(并忽略此設(shè)置)。
If you disable TypeTrees in a project, always rebuild local Addressable groups before building a new player. If you are distributing content remotely, only update content using the same version (including patch number) of Unity that you used to produce your current player and don't make even minor code changes. (When you are juggling multiple player versions, updates, and versions of Unity, you might not find the memory savings from disabling TypeTrees to be worth the trouble.)
譯:如果在項(xiàng)目中禁用TypeTrees,請(qǐng)?jiān)跇?gòu)建新播放器之前始終重新構(gòu)建本地Addressable組。如果您正在遠(yuǎn)程分發(fā)內(nèi)容,請(qǐng)僅使用與生產(chǎn)當(dāng)前播放器所使用的Unity版本相同的版本(包括補(bǔ)丁號(hào)),不要進(jìn)行即使是微小的代碼更改。(當(dāng)您需要處理多個(gè)播放器版本、更新和Unity版本時(shí),您可能不會(huì)發(fā)現(xiàn)禁用TypeTrees的內(nèi)存節(jié)省值得麻煩。)
Table of contents
The table of contents is a map within the bundle that allows you to look up each explicitly included asset by name. It scales linearly with the number of assets and the length of the string names by which they are mapped.
譯:目錄表是Bundle中的一個(gè)映射,允許您按名稱(chēng)查找每個(gè)明確包含的資產(chǎn)。它與資產(chǎn)的數(shù)量和它們映射的字符串名稱(chēng)的長(zhǎng)度成線性關(guān)系。
The size of your table of contents data is based on the total number of assets. You can minimize the amount of memory dedicated to holding table of content data by minimizing the number of AssetBundles loaded at a given time.
譯:您的目錄表數(shù)據(jù)的大小基于資產(chǎn)的總數(shù)。通過(guò)最小化同時(shí)加載的AssetBundle數(shù)量,可以將用于保存目錄表數(shù)據(jù)的內(nèi)存量最小化
Preload table
The preload table is a list of all the other objects that an asset references. Unity uses the preload table to load these referenced objects when you load an asset from the AssetBundle.
譯:預(yù)加載表是一個(gè)列表,其中包含資產(chǎn)引用的所有其他對(duì)象。Unity使用預(yù)加載表在從AssetBundle加載資產(chǎn)時(shí)加載這些引用的對(duì)象
For example, a Prefab has a preload entry for each of its components as well as any other assets it may reference (materials, textures, etc). Each preload entry is 64 bits and can reference objects in other AssetBundles.
譯:例如,一個(gè)Prefab對(duì)于其每個(gè)組件以及其可能引用的任何其他資產(chǎn)(材質(zhì)、紋理等)都有一個(gè)預(yù)加載條目。每個(gè)預(yù)加載條目為64位,并且可以引用其他AssetBundles中的對(duì)象
When an asset references another asset that in turn references other assets, the preload table can become large because it contains the entries needed to load both assets. If two assets both reference a third asset, then the preload tables of both contain entries to load the third asset (whether or not the referenced asset is Addressable or in the same AssetBundle).
譯:當(dāng)一個(gè)資產(chǎn)引用另一個(gè)資產(chǎn),而另一個(gè)資產(chǎn)又引用其他資產(chǎn)時(shí),預(yù)加載表可能會(huì)變得很大,因?yàn)樗虞d兩個(gè)資產(chǎn)所需的條目。如果兩個(gè)資產(chǎn)都引用第三個(gè)資產(chǎn),則兩者的預(yù)加載表都包含用于加載第三個(gè)資產(chǎn)的條目(無(wú)論是否將引用的資產(chǎn)添加到AssetBundle中)
As an example, consider a situation in which you have two assets in an AssetBundle (PrefabA and PrefabB) and both of these prefabs reference a third prefab (PrefabC), which is large and contains several components and references to other assets. This AssetBundle contains two preload tables, one for PrefabA and one for PrefabB. Those tables contain entries for all the objects of their respective prefab, but also entries for all the objects in PrefabC and any objects referenced by PrefabC. Thus the information required to load PrefabC ends up duplicated in both PrefabA and PrefabB. This happens whether or not PrefabC is explicitly added to an AssetBundle.
譯:例如,假設(shè)您在一個(gè)AssetBundle中有兩個(gè)資產(chǎn)(PrefabA和PrefabB),這些預(yù)制品都引用第三個(gè)預(yù)制品(PrefabC),它很大,包含許多組件和引用其他資產(chǎn)。該AssetBundle包含兩個(gè)預(yù)加載表,一個(gè)為PrefabA,一個(gè)為PrefabB。這些表包含其各自預(yù)制的所有對(duì)象的條目,以及PrefabC中的所有對(duì)象和PrefabC引用的任何對(duì)象的條目。因此,加載PrefabC所需的信息最終在PrefabA和PrefabB中重復(fù)。這種情況發(fā)生在是否將PrefabC顯式添加到AssetBundle中
Depending on how you organize your assets, the preload tables in AssetBundles could become quite large and contain many duplicate entries. This is especially true if you have several loadable assets that all reference a complex asset, such as PrefabC in the situation above. If you determine that the memory overhead from the preload table is a problem, you can structure your loadable assets so that they have fewer complex loading dependencies.
譯:根據(jù)您組織資產(chǎn)的方式,AssetBundles中的預(yù)加載表可能會(huì)變得非常大,并包含許多重復(fù)條目。如果您確定預(yù)加載表的內(nèi)存開(kāi)銷(xiāo)是一個(gè)問(wèn)題,則可以構(gòu)造可加載的資產(chǎn),使它們具有更少的復(fù)雜加載依賴(lài)項(xiàng)
Memory implications of loading AssetBundle dependencies
Loading an Addressable asset also loads all of the AssetBundles containing its dependencies. An AssetBundle dependency occurs when an asset in one bundle references an asset in another bundle. An example of this is a material referencing a texture. For more information see?Asset and AssetBundle dependencies.
譯:加載一個(gè)Addressable資產(chǎn)也會(huì)加載包含其依賴(lài)項(xiàng)的所有AssetBundles。當(dāng)一個(gè)Bundle中的資產(chǎn)引用另一個(gè)Bundle中的資產(chǎn)時(shí),就會(huì)發(fā)生AssetBundle依賴(lài)關(guān)系。例如,一個(gè)材質(zhì)引用一個(gè)紋理。有關(guān)更多信息,請(qǐng)參見(jiàn)Asset and AssetBundle dependencies。
Addressables calculates dependencies between bundles at the bundle level. If one asset references an object in another bundle, then the entire bundle has a dependency on that bundle. This means that even if you load an asset in the first bundle that has no dependencies of its own, the second AssetBundle is still loaded into memory.
譯:Addressables在Bundle級(jí)別計(jì)算Bundle之間的依賴(lài)關(guān)系。如果一個(gè)資產(chǎn)引用另一個(gè)Bundle中的對(duì)象,則整個(gè)Bundle都有一個(gè)依賴(lài)關(guān)系。這意味著即使您加載第一個(gè)Bundle中沒(méi)有自己依賴(lài)關(guān)系的資產(chǎn),第二個(gè)AssetBundle仍會(huì)加載到內(nèi)存中。
For Example:
BundleA
?contains Addressable Assets?RootAsset1
?and?RootAsset2
.?RootAsset2
?references?DependencyAsset3
, which is contained in?BundleB
. Even though?RootAsset1
?has no reference to?BundleB
,?BundleB
?is still a dependency of?RootAsset1
?because?RootAsset1
?is in?BundleA
, which has a reference on?BundleB
.
譯:BundleA包含Addressable Assets RootAsset1和RootAsset2。RootAsset2引用DependencyAsset3,該資產(chǎn)包含在BundleB中。即使RootAsset1沒(méi)有引用BundleB,BundleB仍然是RootAsset1的依賴(lài)項(xiàng),因?yàn)镽ootAsset1在BundleA中,而B(niǎo)undleA引用了BundleB
NOTE
Prior to Addressables 1.13.0, the dependency graph was not as thorough as it is now. In the example above, RootAsset1 would not have had a dependency on BundleB. This previous behavior resulted in references breaking when an AssetBundle being referenced by another AssetBundle was unloaded and reloaded. This fix may result in additional data remaining in memory if the dependency graph is complex enough.
譯:在Addressables 1.13.0之前,依賴(lài)關(guān)系圖不如現(xiàn)在那樣徹底。在上面的示例中,RootAsset1不會(huì)依賴(lài)于BundleB。這種先前的行為會(huì)導(dǎo)致當(dāng)一個(gè)AssetBundle被另一個(gè)AssetBundle引用時(shí)被卸載和重新加載時(shí)引用中斷。如果依賴(lài)關(guān)系圖足夠復(fù)雜,則此修復(fù)可能導(dǎo)致額外的數(shù)據(jù)保留在內(nèi)存中
To avoid loading more bundles than are required, you should strive to keep the dependencies between AssetBundles as simple as possible. There are several diagnostic tools included with Addressables that can be used to accomplish this:
譯:為避免加載比所需更多的Bundle,您應(yīng)該盡可能簡(jiǎn)化AssetBundle之間的依賴(lài)關(guān)系。Addressables包括幾個(gè)診斷工具可用于實(shí)現(xiàn)此目的
Analyze Tool? ??分析工具
Build Layout Report? ?構(gòu)建布局報(bào)告