You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效处理压缩对象列表的反序列化、新增与重压缩内存优化

内存优化:流式序列化与压缩的进一步优化

我接手了一段执行以下流程的代码:

  • 从压缩数据字节数组流式解压;
  • 反序列化为对象列表;
  • 向列表添加对象;
  • 序列化列表;
  • 重新压缩为字节数组。

原代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:49:56