如何高效处理压缩对象列表的反序列化、新增与重压缩内存优化
内存优化:流式序列化与压缩的进一步优化
我接手了一段执行以下流程的代码:
- 从压缩数据字节数组流式解压;
- 反序列化为对象列表;
- 向列表添加对象;
- 序列化列表;
- 重新压缩为字节数组。
原代码如下:
private static readonly RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager(); static async Task Main(string[] args) { using (FileStream fileStream = new FileStream("C:\\data.txt", FileMode.Open, FileAccess.Read)) { var dataList = await DecompressData(fileStream); dataList.Add(new MyObject { }); using(var stream = MemoryStreamManager.GetStream()) { await JsonSerializer.SerializeAsync(stream, dataList); stream.Position = 0; var b = await CompressData(stream); } } Console.WriteLine("All done"); } private static async Task<List<SipTraceRecord>> DecompressData(Stream data) { using (var resultStream = MemoryStreamManager.GetStream()) { GZipStream gzip = new GZipStream(data, CompressionMode.Decompress); List<SipTraceRecord> recordsList = await JsonSerializer.DeserializeAsync<List<MyObject>>(gzip); return recordsList; } } private static async Task<byte[]> CompressData(Stream data) { byte[] compressedData; using (var ms = MemoryStreamManager.GetStream()) { using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress)) { data.CopyTo(gzip); compressedData = ms.GetBuffer(); } } return compressedData; }
目前已完成初步内存优化,测试场景下内存占用从原数百MB降至90MB,但await JsonSerializer.SerializeAsync(stream, dataList);环节仍占用数十MB内存,希望能像反序列化那样采用纯流操作优化,避免使用字节数组;另外data.CopyTo(gzip);会复制数据,而压缩后内存占用实际小于1MB。
优化方案
核心思路是将序列化与压缩环节合并为纯流式处理,跳过中间的MemoryStream,让序列化的内容直接流入压缩流,彻底避免中间大内存占用。
修改后的完整代码
private static readonly RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager(); static async Task Main(string[] args) { using (FileStream fileStream = new FileStream("C:\\data.txt", FileMode.Open, FileAccess.Read)) { var dataList = await DecompressData(fileStream); dataList.Add(new MyObject { }); // 直接序列化到压缩流,全程流式处理 using (var compressedStream = MemoryStreamManager.GetStream()) { using (var gzipStream = new GZipStream(compressedStream, CompressionMode.Compress, leaveOpen: true)) { // 序列化内容直接写入压缩流,无中间内存缓存 await JsonSerializer.SerializeAsync(gzipStream, dataList); } // 提取有效压缩数据(仅包含实际压缩后的字节) var compressedBytes = compressedStream.ToArray(); // 此处可处理压缩后的字节数组,例如写入文件或传输 } } Console.WriteLine("All done"); } private static async Task<List<MyObject>> DecompressData(Stream data) { // 简化反序列化流程,直接从GZipStream读取,无需中间MemoryStream using (var gzipStream = new GZipStream(data, CompressionMode.Decompress)) { return await JsonSerializer.DeserializeAsync<List<MyObject>>(gzipStream) ?? new List<MyObject>(); } } // 可选:复用的压缩方法,直接接收对象并返回压缩字节数组 private static async Task<byte[]> CompressAndSerializeAsync<T>(T data) { using (var memoryStream = MemoryStreamManager.GetStream()) { using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, leaveOpen: true)) { await JsonSerializer.SerializeAsync(gzipStream, data); } return memoryStream.ToArray(); } }
关键优化点
- 流式序列化+压缩:将
JsonSerializer.SerializeAsync的输出直接指向GZipStream,序列化产生的每一段数据都会立即被压缩,完全不需要将整个序列化后的JSON内容缓存到内存中,彻底消除了序列化环节的大内存占用。 - 简化反序列化流程:原代码中
resultStream是冗余的,JsonSerializer.DeserializeAsync可以直接从GZipStream读取数据,移除后减少不必要的内存开销。 - 修复内存流缓冲区问题:原代码使用
ms.GetBuffer()会返回包含未使用空间的完整缓冲区,改用ToArray()仅提取实际有效的压缩数据,避免无效内存占用。 - 避免数据复制:原流程中
data.CopyTo(gzip)的复制操作被彻底消除,数据直接从序列化环节流入压缩环节,节省内存和CPU资源。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

