1、镜像加载和卸载
当使用可寻址资源时,确保适当的内存管理的主要方法是正确镜像加载和卸载调用。如何执行取决于您的资源类型和加载方法。但是,在所有情况下,释放方法都可以接受已加载的资源,也可以采用加载返回的操作句柄。
例如:在场景创建过程中(如下所述),加载返回一个 AsyncOperationHandle,您可以通过返回的句柄或通过跟上handle.Result(在本例中是一个 SceneInstance)来释放它。
1.资源加载
要加载资源,请使用Addressables.LoadAssetAsync或Addressables.LoadAssetsAsync。
这会将资源加载到内存中而不实例化它。每次执行加载调用时,都会为每个加载的资源中添加一个引用计数。如果使用相同的地址调用LoadAssetAsync三次,将获得AsyncOperationHandle结构的三个不同实例,所有实例都引用相同的底层操作。该操作对应的资源的引用计数为3。如果加载成功,则生成的AsyncOperationHandle结构将在.Result属性中包含资源。您可以通过Unity的内置实例化方法使用加载的资源进行实例化,该方法不会增加Addressables引用计数。
要卸载资源,请使用Addressables.Release方法减少引用计数。当给定资源的引用计数为零时,该资源已准备好卸载,并减少了任何依赖项的引用计数。
注意:资源可能会立即卸载,也可能不会立即卸载,取决于现有的依赖关系。有关更多信息,请阅读有关
何时清除内存
的部分。
2.场景加载
要加载场景,使用Addressables.LoadSceneAsync。您可以使用此方法以关闭所有打开场景的Single 模式或Additive 模式来加载场景(有关更多信息,请参阅关于
场景模式加载的文档
)。
要卸载场景,请使用Addressables.UnloadSceneAsync,或在Single模式下打开一个新的场景。您可以使用Addressables接口或使用SceneManager.LoadScene或SceneManager.LoadSceneAsync方法来打开新场景。打开一个新的场景将关闭当前场景,并适当减少引用计数。
3.GameObject实例化
要加载和实例化GameObject资原,请使用Addressables.InstantiateAsync。这将实例化通过指定location参数定位的预制件。Addressables系统将加载Prefab及其依赖项,从而增加所有关联资源的引用计数。
对同一个地址调用三次InstantiateAsync 会导致所有相关资源的引用计数为3。但是,与三次调用LoadAssetAsync不同,每次InstantiateAsync调用都返回AsyncOperationHandle指向唯一操作的指针。这是因为每个结果InstantiateAsync都是唯一的实例。InstantiateAsync与其他加载调用之间的另一个区别是可选trackHandle参数。设置false为时,AsyncOperationHandle在释放实例时必须保持使用。这效率更高,但是需要更多的开发工作。
要销毁实例化的GameObject,请使用Addressables.ReleaseInstance或关闭包含实例化对象的Scene。可以在Additive或Single模式下加载(因此关闭)此场景。也可以使用Addressables或SceneManagementAPI 加载此场景。如上所述,如果设置trackHandle为false,则只能Addressables.ReleaseInstance使用手柄进行调用,而不能使用实际的GameObject进行调用。
注意:如果调用Addressables.ReleaseInstance的不是使用AddressablesAPI 创建的实例,也不是使用trackHandle==false创建的实例,则系统会检测到这一点并返回false,以指示该方法无法释放指定的实例。在这种情况下,实例不会被销毁。
Addressables.InstantiateAsync有一些相关的开销,因此,如果您需要每帧实例化同一对象数百次,请考虑通过AddressablesAPI 加载,然后通过其他方法实例化。在这种情况下,您可以调用Addressables.LoadAssetAsync,然后保存结果并调用GameObject.Instantiate()该结果。这允许以同步方式灵活地调用Instantiate。缺点是可寻址系统不知道您创建了多少个实例,如果管理不当,可能会导致内存问题。
例如,引用纹理的Prefab将不再具有要引用的有效加载纹理,从而导致渲染问题(或更糟)。这类问题很难跟踪,因为您可能不会立即触发内存卸载(请参阅下面有关
清除内存的部分
)。
4.数据加载
不需要释放的AsyncOperationHandle.Result接口,而需要释放操作本身。例如:包括Addressables.LoadResourceLocationsAsync和Addressables.GetDownloadSizeAsync。它们将加载您可以访问的数据,直到释放操作为止。此版本应通过进行Addressables.Release。
5.后台互动
在AsyncOperationHandle.Result字段中不返回任何内容的操作具有可选参数,可在完成时自动释放操作句柄。如果在完成操作后不再需要这些操作手柄之一,则将autoReleaseHandle参数设置为true,确保清理了操作句柄。如果希望在操作句柄完成后检查其状态,则需要autoReleaseHandle为false。这些接口的示例有:Addressables.DownloadDependenciesAsync和Addressables.UnloadScene。
2、可寻址事件查看器(Addressables Event Viewer)
使用 Addressables Event Viewer 窗口来监视所有可寻址系统操作的引用计数。要访问编辑器中的窗口,请选择Window > Asset Management > Addressables > Event Viewer。
重要说明:为了在事件查看器中查看数据,必须在AddressableAssetSettings对象的检查器中启用发送事件分析器(Send Profiler Events)设置。
事件查看器中提供了以下信息:
- 白色竖线表示发生加载请求的帧。
- 蓝色背景指示当前正在加载资源。
-
图中的绿色部分表示资源的当前参考计数。
请注意,事件查看器仅与引用计数有关,与内存消耗无关(有关更多信息,请参见下面的“ 清除内存”部分)。
在资源栏下列出的内容中,每帧以下内容将显示一行:
- FPS:每秒帧数。
- MonoHeap:正在使用的RAM数量。
- 事件计数:一帧中事件的总数。
- 资源请求:显示一段时间内某个工序的参考计数。如果资源请求具有任何依存关系,则会出现一个三角形,您可以单击该三角形以查看子级的请求操作。
您可以单击左箭头和右箭头以逐帧浏览帧,或单击当前(Current)跳到最新的帧。按+按钮可展开一行以获取更多详细信息。
事件查看器中显示的信息与您用来创建播放模式数据的
构建脚本
有关。
使用事件查看器时,请避免使用 Asset Database 构建的脚本,因为它不考虑资源之间的任何共享依赖关系。请改用模拟组(Simulate Groups)脚本或使用现有构建脚本,但是后者更适合于事件查看器,因为它可以更准确地监视引用计数。
3、何时清除内存?
不再引用的资源(在探查器中蓝色部分的末尾表示)并不一定意味着该资源已卸载。常见的适用场景涉及资源 bundle 中的多个资源。例如:
- 资源 bundle (stuff) 中有三个资源(tree,tank和cow)。
- 当tree加载时,分析器为tree显示一个引用计数,为stuff显示一个引用计数。
- 稍后,在tank加载时,分析器为tree和tank都显示一个引用计数,为stuff bundle 显示两个引用计数。
- 如果释放tree,则引用计数变为零,蓝色条消失。
在此示例中,此时tree实际上并未卸载资源。您可以加载资源 bundle 或其部分内容,但不能部分卸载资源 bundle。在stuff完全卸载之前,不会卸载 bundle 任何资源。这个规则的例外是引擎接口
Resources.UnloadUnusedAssets
。在上述情况下执行此方法将导致tree卸载。因为可寻址系统无法识别这些事件,所以分析器图仅反映了可寻址引用计数(而不是内存保存的内容)。
请注意,如果您选择使用Resources.UnloadUnusedAssets,这是一个非常慢的操作,应该只在一个不会影响正常显示的屏幕上调用(比如加载屏幕)。