Android 上的 Unreal 游戏内存优化

内存用量在 Android 游戏开发中至关重要。使用 Unreal Engine 开发游戏时,您必须持续分析和跟踪内存状态,以满足这些内存限制,防止内存泄漏和过度分配内存。

通过减少着色器排列组合来节省内存

通过移除 LightingMobile Shader Permutation Reduction 中未使用的选项,您可以最大限度地减少着色器排列组合的数量。这可显著减小应用打包大小,减少流水线状态对象 (PSO) 的数量,并防止创建未使用的渲染目标或虚拟纹理,从而减少运行时内存并提高性能。

如需了解详情,请参阅移动设备性能指南中的移动设备的杂项光照和着色器排列组合缩减设置部分。

资源加载优化

使用硬引用加载资源时,即使是游戏中未使用的关联内容也会同时加载到内存中,从而导致不必要的内存消耗。特别是,如果蓝图中的继承使用不当,则会分配来自父级的非必要 CDO(类默认对象),从而浪费大量内存。在这种情况下,请应用以下技巧来优化内存用量。

  • C++ 代码定义和数据分离:使用 C++ 代码中的 UCLASS(Abstract) 定义核心功能,将其与实际数据分离。
  • 尽量减少继承:仅在叶节点蓝图中执行数据设置和资源加载。
  • 使用间接引用:使用 TSoftObjectPtr 应用间接属性引用,以避免硬引用,并仅在运行时需要时异步加载资源。
  • 注意事项:使用异步资源加载时,请确保在加载完成回调期间执行有效性检查 (IsValid),以防止出现悬空指针或崩溃。

如需了解详情,请参阅引用资源使用 UE5 构建移动游戏

智能指针和垃圾回收 (GC) 调整

在 C++ 开发期间使用智能指针(例如 TSharedPtrTWeakPtr)可防止悬空指针和内存泄漏。此外,使用 TWeakPtr 可避免创建对对象的强引用,因此垃圾收集器在收集对象时不会递增引用计数。这样可以防止不必要的对象存活,并降低 GC 需要遍历的引用图的复杂性,从而显著减少 GC 开销。在移动环境中,可以通过在级别转换或不活动期间(例如没有用户互动时)触发 ForceGarbageCollection 来有效管理垃圾收集。此外,当游戏处于可感知状态或后台状态(例如 onStop 回调)时,也可以触发此操作,确保在不影响活跃游戏体验的情况下执行垃圾回收。

如需了解详情,请参阅垃圾回收智能指针

使用重定位表压缩进行优化

重定位表压缩是一种优化方法,可压缩 ELF 二进制文件中的重定位表大小,以减小 Android 二进制文件的大小。当 Android minSDKVersion 为 23 或更高版本时,支持 APS 重定位表压缩;当 minSDKVersion 为 28 或更高版本时,还支持 RELR 重定位表压缩,从而显著减小 .so 文件的大小。这不仅会减小下载大小,还会减少运行时 .so mmap 的内存占用,从而缓解总体物理内存压力。

AndroidToolChain.cs

if (MinSDKVersion >= 28)
{
  Result += " -Wl,--pack-dyn-relocs=android+relr,--use-android-relr-tags";
}
else if (MinSDKVersion >= 23)
{
  Result += " -Wl,--pack-dyn-relocs=android";
}

如需应用此优化,请验证项目设置中的启用重定位表压缩bEnableAdvancedBinaryCompression 变量是否已启用。

如需检查 RELR 压缩是否已正确应用于二进制文件,请使用 Android NDK 中的 llvm-readelf 工具。

llvm-readelf -d libUnreal.so | grep RELR

实际上,构建 Unreal Engine 的 ThirdPerson 模板并使用 dumpsys meminfo 进行测量,可确认 .so mmap 的 PSS 和 RSS 使用量有所减少。

类别 PSS 总计 私有脏页 私密清理 交换脏页 RSS 总计
.so mmap 压缩前 170,210 24,360 141,564 0 242,204
.so mmap 压缩后 138,031 24,748 108,976 0 210,140

纹理压缩

使用适当的纹理压缩来减少整体图形资源的内存占用。使用移动标准自适应可伸缩纹理压缩 (ASTC),与 ETC2 相比,在相同大小下可提供出色的视觉质量,或者在相同的视觉质量目标下实现更高的压缩率,同时降低内存消耗。

纹理流式传输优化

在移动环境中,直接使用默认的 Unreal Engine 纹理流式传输池大小可能会预先分配相对于设备规格过大的内存池。因此,最好定义移动设备配置文件,并根据设备内存限制选择合适的纹理流式传输池大小。此外,还可以使用编辑器的统计信息功能来检查纹理统计信息,并识别运行时占用极高分辨率的高内存消耗纹理。将此功能与所需纹理分辨率功能搭配使用,可对要求较高的纹理强制执行限制或降低最大分辨率,从而将资源使用量控制在分配的纹理内存缓冲区内。