前言
最近在優化一個項目,發現使用asp.net api時候發現內存占用過高。從中發現有某處地方直接使用Newtonsoft.json 的JArray對象序列化后返回HttpResponseMessage,
也有一部分是直接返回JArray,后來懷疑JObject內存占用過高,此函數是使用隊列來生成報表文件數據量比較大,而傳輸方式使用Json,解析為了方便而直接使用JArray.Parse,
改為反序列化IList<IDictionary<string,object>>后有所改觀;經筆者測試發覺JObject產生的對象確實對內存占用過多而無法准確釋放。
測試
Release測試1W條數據每行30列
一、NewtonsoftTest
string guid = Guid.NewGuid().ToString(); JArray array = new JArray(); for (int i=0; i < 10000; i++) { JObject obj = new JObject(); for (int c=1; c <= 10; c++) { obj["Id_" + c] = i; obj["Guid_" + c] = guid; obj["Sex_" + c] = i % 2 == 0; } array.Add(obj); }
進程內存占用80M左右
二、FCLCollectionTest
string guid = Guid.NewGuid().ToString(); IList<IDictionary<string,object>> array = new List<IDictionary<string, object>>(); for (int i=0; i < 10000; i++) { var obj = new Dictionary<string, object>(); for (int c=1; c <= 10; c++) { obj["Id_" + c] = i; obj["Guid_" + c] = guid; obj["Sex_" + c] = i % 2 == 0; } array.Add(obj); }
進程內存占用28M左右
三、JsonWriterTest
string guid = Guid.NewGuid().ToString(); var jsonWriter = new JsonTextWriter(new StringWriter(CultureInfo.InvariantCulture)); jsonWriter.Formatting = Formatting.None; jsonWriter.WriteStartArray(); for (int i=0; i < 10000; i++) { jsonWriter.WriteStartObject(); for (int c=1; c <= 10; c++) { jsonWriter.WritePropertyName("Id_" + c); jsonWriter.WriteValue(i); jsonWriter.WritePropertyName("Guid_" + c); jsonWriter.WriteValue(guid); jsonWriter.WritePropertyName("Sex_" + c); jsonWriter.WriteValue(i % 2 == 0); } jsonWriter.WriteEndObject(); } jsonWriter.WriteEndArray();
進程內存占用24M左右
分析
因筆者時間不多,只做了部分測試,使用CLRProfiler來分析程序的內存分配
1.使用Dictionary<string,object>來作為JObject的替代測試
2.使用Newtonsoft.json JObject測試
可以從圖中看到JProperty JValue JToken占用內存情況,而String,int,boolean屬於測試數據的內存分配
3.使用JsonTextWriter測試如下
以上測試只截取部分圖例,更深層次讀者可以下載源代碼自行調試觀看。
結語
JsonTextWriter和拼寫字符串區別不大,如果只是把json以字符串形式返回,盡量使用JsonTextWriter來處理,從而減少對象的生成。
JObject可以使用字典或者實體模型方式來代替,然后使用JsonConvert.SerializeObject 序列化。