如何解决ProtoBuf反序列化中GC高的问题

如何解决ProtoBuf反序列化中GC高的问题

1)如何解决ProtoBuf反序列化中的GC高的问题
2)如何在编辑器中集成实时查看Mipmap使用层级工具


这是第455篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地掌握和学习。

UWA社区主页:community.uwa4d.com
UWA QQ群:793972859

From UWA AI 问答

Q:有没有比较好的解决ProtoBuf反序列化GC的方案?

A:在Unity项目中,使用ProtoBuf进行反序列化时产生的GC主要来源于两方面:内存分配的临时对象(如字符串、装箱操作)和反射机制的低效调用。以下是针对这些问题的成熟且高效的解决方案。

1. 使用[Preserve]与强制AOT初始化避免反射GC
IL2CPP环境下,由于AOT编译限制,ProtoBuf生成代码中的泛型反射方法可能未被提前编译,导致运行时尝试动态调用而崩溃或触发大量GC。为确保所有必要的反射初始化代码被保留:

using Google.Protobuf.Reflection;
using UnityEngine.Scripting;

[Preserve]
private void ForceReflectionInitialization()
{
    // 显式调用每个消息类型的ForceReflectionInitialization
    FileDescriptor.ForceReflectionInitialization<MyMessage1>();
    FileDescriptor.ForceReflectionInitialization<MyMessage2>();
    // 添加所有用到的消息类型
}

此方法无需调用,仅需存在即可被链接器保留。配合[Preserve]特性防止代码剥离,可有效解决因AOT缺失导致的异常及性能损耗。

2. 避免字符串与值类型装箱,实现0GC解析辅助
参考TMP文本优化思路,可对ProtoBuf解析过程中的输出处理进行定制,减少中间对象分配。例如,在自定义的日志系统或UI更新中直接复用StringBuilder写入,而非通过ToString()创建新字符串。

  • 全局复用StringBuilder用于格式化输出。
  • 对数字字段转字符串时,采用无GC的NumberFormatter.NumberToString(ref char[] buffer, int value)方式,将结果写入预分配字符数组。
  • 自定义IFormatter实现,绕过装箱操作,直接处理基本类型。

3. 使用原生字节访问替代高开销API
对于高频解析场景(如网络同步),考虑绕过完整反序列化流程,直接读取字节流中的关键字段:

// 使用CodedInputStream直接读取特定字段
var input = CodedInputStream.CreateInstance(data);
while (!input.IsAtEnd)
{
    var tag = input.ReadTag();
    switch (tag.FieldNumber)
    {
        case 1:
            int hp = input.ReadInt32();
            break;
        // 只解析必要字段
    }
}

这种方式跳过对象构建开销,适用于只关心部分数据的轻量级更新。

4. 缓冲池管理Message实例
频繁创建/销毁ProtoBuf对象会加剧GC压力。建议使用对象池缓存常用Message实例:

public class MessagePool<T> where T : IMessage, new()
{
    private static readonly Stack<T> _pool = new();

    public static T Get() => _pool.Count > 0 ? _pool.Pop() : new T();

    public static void Release(T msg) { _pool.Push(msg); }
}

结合Clear()重置状态后复用,显著降低内存分配频率。

综上,核心策略是:强制AOT初始化 + 禁止代码剥离 + 复用缓冲区 + 裁剪不必要的反序列化流程。这些方法已在多个上线项目中验证,能有效将ProtoBuf相关GC降至接近零。

分享链接:
https://www.uwa4d.com/main/uwa-shared.html?shareId=228136d0-985e-4af9-8c83-f84b04834677


From 问答社区

Q:最近在优化贴图内存,请问有没有开源库或者计算方法,可以检测贴图在屏幕上的占比情况?

比如,一张1024的贴图,如果渲染时一直使用很高层级的Mipmap,就说明其实不需要1024这么高的分辨率。我记得有种工具还能通过颜色可视化,我主要想集成在编辑器里面实时查看修改。

A:URP里面编写Mipmap工具可以参考以下链接:
Scene View Debug Modes in the Unity URP
A way to visualize mip levels

欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6925280c682c7e5cd61bfb76

无论是社区里开发者们的互助讨论,还是AI基于知识沉淀的快速反馈,核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区,希望这些从真实开发场景中提炼的经验,能直接帮你解决当下的技术卡点,也让你在遇到同类问题时,能更高效地找到破局方向。

封面图来源于网络


今天的分享就到这里。生有涯而知无涯,在漫漫的开发周期中,我们遇到的问题只是冰山一角,UWA社区愿伴你同行,一起探索分享。欢迎更多的开发者加入UWA社区。

UWA官网:www.uwa4d.com
UWA社区:community.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859