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

如何避免LOH分配并解决ArrayPool<T>租聘数组过大的上传问题?

解决ArrayPool分块上传时数组尺寸过大的问题

核心问题是ArrayPool<T>.Shared.Rent()返回的数组可能大于请求的chunkSize,但我们只需要用到前chunkSize(或实际读取的字节数)的部分,不需要传递整个数组。以下是两种可行的解决方案,都不会分配新数组:

方案1:基于实际读取字节数传输切片

利用ReadAsync的返回值获取实际读取的字节数,然后仅传输对应长度的内存片段,完全避免传递多余的字节:

while (someflag)
{
    reader.Seek(counterHowMuchIsTransferred, SeekOrigin.Begin);
    var buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
    try
    {
        // 限制读取范围为请求的chunkSize,避免读取到数组多余部分
        int bytesRead = await reader.ReadAsync(buffer.AsMemory(0, chunkSize), cancellationToken);
        if (bytesRead == 0) break; // 文件已读取完毕

        // 上传时仅传递实际读取的字节片段,而非整个数组
        await UploadChunkAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
        counterHowMuchIsTransferred += bytesRead;

        // 如果是最后一块(读取字节数小于chunkSize),终止循环
        if (bytesRead < chunkSize) someflag = false;
    }
    finally
    {
        // 务必归还数组到池,防止内存泄漏
        ArrayPool<byte>.Shared.Return(buffer);
    }
}

方案2:严格保证块大小(除最后一块)

如果API要求除最后一块外,其余块必须严格等于chunkSize,可以循环读取直到填满chunkSize或文件结束:

while (someflag)
{
    reader.Seek(counterHowMuchIsTransferred, SeekOrigin.Begin);
    var buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
    try
    {
        int totalBytesRead = 0;
        // 循环读取直到凑够chunkSize字节或文件结束
        while (totalBytesRead < chunkSize)
        {
            int bytesRead = await reader.ReadAsync(
                buffer.AsMemory(totalBytesRead, chunkSize - totalBytesRead), 
                cancellationToken
            );
            if (bytesRead == 0) break;
            totalBytesRead += bytesRead;
        }

        if (totalBytesRead == 0) break;

        // 传输对应长度的内存片段
        await UploadChunkAsync(buffer.AsMemory(0, totalBytesRead), cancellationToken);
        counterHowMuchIsTransferred += totalBytesRead;

        // 最后一块不足chunkSize,终止循环
        if (totalBytesRead < chunkSize) someflag = false;
    }
    finally
    {
        ArrayPool<byte>.Shared.Return(buffer);
    }
}

关键注意事项

  • 无论哪种方案,都要在finally块中归还数组到ArrayPool,否则会造成内存泄漏
  • 使用AsMemory()创建内存切片不会分配新数组,只是生成一个指向原数组指定范围的视图,完全符合避免LOH分配的需求
  • 永远不要直接传递ArrayPool借出的整个数组,必须根据实际读取长度切片后再传输

内容的提问来源于stack exchange,提问作者Expressingx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:50:27