如何让ArrayPool返回指定精确大小的数组?
从ArrayPool获取精确大小数组的需求与问题
在.NET Standard 2.0中,ArrayPool<byte>.Rent(int minLength)方法返回的byte[]大小至少为minLength,但实际可能更大。我的流式框架属于内存/IO密集型场景,其中部分遗留加密提供程序仅接受固定特定大小(如256、512)的byte[];同时如果数组进入Gen2会引发内存膨胀问题。
由于方法签名限制,无法使用Span<T>/ArraySegment<T>等类型来适配。我希望复用ArrayPool的对象池机制,不想自行实现对象池,同时允许使用unsafe代码,曾考虑过封送/映射同一块内存,但不确定可行性。
尝试的解决方案:修改数组长度的辅助类
在他人协助下编写了一个辅助类,通过unsafe方式修改数组的长度字段,返回精确大小的可释放数组对象:
public static class ArrayPoolHelper { /// <summary> /// 从指定数组池获取精确大小的可释放缓冲区 /// </summary> /// <param name="pool">要使用的数组池</param> /// <param name="size">期望的缓冲区大小</param> /// <typeparam name="T">数组元素类型</typeparam> /// <returns>包含精确大小缓冲区的可释放对象</returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static IRentedArray<T> RentExact<T>(ArrayPool<T> pool, int size) { if (pool == null) throw new ArgumentNullException(nameof(pool)); if (size <= 0) { throw new ArgumentOutOfRangeException(nameof(size)); } var rented = pool.Rent(size); var originalSize = UnsafeChangeLength(rented, (uint)size); return new RentedArray<T>(rented, originalSize, pool); } private static uint UnsafeChangeLength<T>(T[] array, uint newLength) { if (array.Length == newLength) return newLength; var unsafeRented = Unsafe.As<RawArrayData>(array); var originalLength = unsafeRented.Length; unsafeRented.Length = newLength; return originalLength; } private sealed class RentedArray<T> : IRentedArray<T> { private readonly uint _originalSize; private readonly ArrayPool<T> _pool; private readonly T[] _original; private bool _disposed; public T[] Data => _disposed ? throw new ObjectDisposedException(ToString()) : _original; public RentedArray(T[] original, uint originalSize, ArrayPool<T> pool) { _originalSize = originalSize; _pool = pool; _original = original; } public void Dispose() { if (_disposed) return; UnsafeChangeLength(_original, _originalSize); _pool.Return(_original, true); _disposed = true; } } private sealed class RawArrayData { public uint Length; } }
使用方式:
using var rented = ArrayPoolHelper.RentExact(pool, 123); Assert.AreEqual(rented.Data.Length, 123);
方案问题:并行场景下的CLR错误
后续测试发现,上述通过unsafe修改数组长度的方式,在并行调用时会抛出CLR错误,说明直接修改数组的内部长度字段并非安全可行的方案。
内容的提问来源于stack exchange,提问作者eocron
相关产品推荐
相关产品推荐

