日前,團隊在壓測的中發現以異常的資源損耗,細看才發現誤用了 XmlSerializer 的動態生成

開發環境
- Windows 11 Pro
- Rider 2025.1.4
- .NET 9
先來看看有問題的寫法
[Serializable]
[XmlRoot("Person")]
public class TestPerson
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Age")]
public int Age { get; set; }
[XmlElement("Email")]
public string Email { get; set; }
[XmlElement("CreatedDate")]
public DateTime CreatedDate { get; set; }
}
不好的寫法
public class BadXmlSerializerService
{
public string Serialize(TestPerson person)
{
// ❌ 每次都建立新的 XmlSerializer,並且使用 XmlRootAttribute
var xmlRoot = new XmlRootAttribute("Person");
var serializer = new XmlSerializer(typeof(TestPerson), xmlRoot);
using var writer = new StringWriter();
serializer.Serialize(writer, person);
return writer.ToString();
}
}
API 端點
[HttpGet("bad")]
public async Task<ActionResult> BadSerialize()
{
var person = new TestPerson
{
Name = "yao-bad",
Age = 18,
Email = "yao-bad@aa.bb",
CreatedDate = DateTime.Now
};
var xml = _badService.Serialize(person);
return this.Ok(xml);
}
用 Release Build 執行 Web API,並用 k6 施壓,施壓腳本,才運行一段時間就可以看到 Unmanaged memory 隨著時間成長

記憶體使用量來到了 2192 MB

趕緊取得記憶體快照,雙擊 Snapshot #1,看分析報告

透過 AI 分析一下哪裡出了問題,這裡就不贅述怎麼使用。


AI 很快的就給出了建議,官方文檔也有寫到,只有下列建構函式會重複使用元件,
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
其他的建構函式會有記憶體洩漏的問題,參考:
System.Xml.Serialization.XmlSerializer 類別 - .NET | Microsoft Learn

修改後的寫法
這裡用 ConcurrentDictionary 來快取 XmlSerializer 物件
public class GoodXmlSerializerService
{
// ✅ 使用 ConcurrentDictionary 快取 XmlSerializer 實例
private static readonly ConcurrentDictionary<Type, XmlSerializer> Cache
= new ConcurrentDictionary<Type, XmlSerializer>();
private static XmlSerializer GetSerializer(Type type, XmlRootAttribute xmlRoot)
{
return Cache.GetOrAdd(type, _ => new XmlSerializer(type, xmlRoot));
}
public string Serialize(TestPerson person)
{
var xmlRoot = new XmlRootAttribute("Person");
var serializer = GetSerializer(typeof(TestPerson), xmlRoot);
using var writer = new StringWriter();
serializer.Serialize(writer, person);
return writer.ToString();
}
}
API 端點
[HttpGet("good")]
public async Task<ActionResult> GoodSerialize()
{
var person = new TestPerson
{
Name = "yao-good",
Age = 18,
Email = "yao-good@aa.bb",
CreatedDate = DateTime.Now
};
var xml = _goodService.Serialize(person);
return this.Ok(xml);
}
用 Release Build 執行 Web API,並用 k6 施壓,施壓腳本,已經可以看到 Unmanaged memory 使用狀況已經改善很多了

快照記憶體結果


比較這兩個記憶體快照的結果,更細部的比較可以透過 Comapre取得

範例位置
sample.dotblog/Xml/Lab.XmlSerializerPerformance at master · yaochangyu/sample.dotblog
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET