懒人有各种各样的偷懒手段,主要是他想偷懒。
最近又扎入了阅读的深渊而不能自拔,一不小心意识到已是八月,索性就偷闲对最近开发中的Bug定位修改做个记录。
公司发展经年,已上线多个项目,有些项目也都上线了多个版本,伴随着跟破解玩家斗争的不断升级,终于在五年年前某个项目的某个版本中,开始引入了内存加密的手段,然后作为一只喜欢偷懒的程序狗,对内存加密的手段及方式一再进行升级和重构,为了达到尽可能的一劳永逸,终于在四年前进行了常用基本类型的加密封装:EncryptInt,EncrypyFloat,之后每每交付部门的各项目人员进行项目升级,于是开启了一段Bug的传奇。。。
在公司最新的项目之前(Unity4/5),项目中使用的序列化插件一直都是NewtonJson.Net,github上的开源项目,Unity商店中也有封装好的版本,当然为了偷懒的方便,前期未封装类型前(逐个重要信息加密时期),直接使用了别人上传的dll;为针对后期封装为加密类型后的偷懒,也对应的进行了插件的升级,这样插件升级后,我们即可以使用JsonConvert.DefaultSettings的方式进行加密类型数据的全局序列化设置:
1 JsonSerializerSettings tSetting = new JsonSerializerSettings(); 2 var tIntConverter = new JsonCustomIntConvert(); 3 var tFloatConverter = new JsonCustomFloatConvert(); 4 JsonConvert.DefaultSettings = new System.Func<JsonSerializerSettings>(() => 5 { 6 tSetting.Converters.Add(tIntConverter); 7 tSetting.Converters.Add(tFloatConverter); 8 return tSetting; 9 });
其中JsonCustomIntConvert/JsonCustomFloatConvert为我们针对加密类型EncryptInt/EncrypyFloat定义的序列化转换器:
1 public class JsonCustomIntConvert : JsonConverter 2 { 3 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 4 { 5 if(reader.TokenType == JsonToken.Null) 6 { 7 return 0; 8 } 9 else if(reader.TokenType == JsonToken.String) 10 { 11 return (EncryptInt)int.Parse(reader.Value.ToString()); 12 } 13 else if(reader.TokenType == JsonToken.Integer) 14 { 15 return (EncryptInt)Convert.ToInt32(reader.Value); 16 } 17 else 18 { 19 return 0; 20 } 21 } 22 23 public override bool CanConvert(Type objectType) 24 { 25 return objectType == typeof(EncryptInt); 26 } 27 28 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 29 { 30 int tValue = (EncryptInt)value; 31 writer.WriteValue(tValue); 32 } 33 }
于是问题就这么引入进来了。。
且卡顿的点应该是在进行数据存档。
下进行Bug的复现测试:
及Bug的固有性,于是将打点处的序列化改为序列化随机初始化的有50个固定数据的List,果然问题依旧,问题出在序列换的阶段。
题在递归发生之前/序列化之前),通过简单分析怀疑是泄露问题,所以针对可能的Add、Insert等进行了针对性的查找分析以及缓存修改测试,最终两方定位发现问题出在JsonSerializer.CreateDefault(settings)处,其底层实现为:
1 public static JsonSerializer CreateDefault() 2 { 3 // copy static to local variable to avoid concurrency issues 4 Func<JsonSerializerSettings> defaultSettingsCreator = JsonConvert.DefaultSettings; 5 JsonSerializerSettings defaultSettings = (defaultSettingsCreator != null) ? defaultSettingsCreator() : null; 6 7 JsonSerializer tSerializer = Create(defaultSettings); 8 return tSerializer; 9 }
可以看到每次序列化都会操作JsonConvert.DefaultSettings,即会不断的tSetting.Converters中添加新的序列化器,从而导致泄露。只需简单修改如下即可:
1 var tIntConverter = new JsonCustomIntConvert(); 2 var tFloatConverter = new JsonCustomFloatConvert(); 3 JsonConvert.DefaultSettings = new System.Func<JsonSerializerSettings>(() => 4 { 5 JsonSerializerSettings tSetting = new JsonSerializerSettings(); 6 tSetting.Converters.Add(tIntConverter); 7 tSetting.Converters.Add(tFloatConverter); 8 return tSetting; 9 });
修改后的及时测试及定期的code review不可懈怠!