agg*_*k02 33 .net memorystream getbuffer
我知道,GetBuffer()
在C#/一个MemoryStream.NET必须小心使用,因为,作为文档描述了这里,有可能在年底未使用的字节,所以你必须要确保只在第一MemoryStream的期待缓冲区中的.Length字节.
但后来我遇到了一个案例,其中缓冲区开头的字节是垃圾!实际上,如果您使用像反射器这样的工具并查看ToArray()
,您可以看到:
public virtual byte[] ToArray()
{
byte[] dst = new byte[this._length - this._origin];
Buffer.InternalBlockCopy(this._buffer, this._origin, dst, 0,
this._length - this._origin);
return dst;
}
Run Code Online (Sandbox Code Playgroud)
所以要对返回的缓冲区做任何事情GetBuffer()
,你真的需要知道_origin.唯一的问题是_origin是私有的,没有办法实现它......
所以我的问题是-什么是使用GetBuffer()
在MemoryStream()
无将MemoryStream是如何构造的一些先验知识(这是什么套_origin)?
(正是这个构造函数,只有这个构造函数,设置原点 - 当你想要一个字节数组的MemoryStream从字节数组中的特定索引开始时:
public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
Run Code Online (Sandbox Code Playgroud)
)
For*_*gic 16
答案是在GetBuffer()MSDN文档中,您可能错过了它.
当您创建一个MemoryStream
不提供字节数组(byte[]
)时:
它创建了一个初始化为零的可扩展容量.
换句话说,byte[]
当Write
在Stream上进行调用时,MemoryStream将引用具有适当大小的a .
因此,GetBuffer()
您可以直接访问底层数组并读取它.
当您处于不知道其大小的情况下将收到流时,这可能很有用.如果收到的流通常非常大,那么呼叫GetBuffer()
要比调用ToArray()
引擎盖下的数据复制要快得多,见下文.
要仅获取缓冲区中的数据,请使用ToArray方法; 但是,ToArray会在内存中创建数据副本.
我想知道你可能在一开始就调用GetBuffer()来获取垃圾数据,它可能是两次Write
调用之间,第一次调用的数据会被垃圾收集,但我不确定是否会发生这种情况.
小智 12
如果您确实想要访问内部_origin值,可以使用MemoryStream.Seek(0,SeekOrigin.Begin)调用.返回值将完全是_origin值.
Unk*_*own 11
ToArray()是GetBuffer()的替代品.但是,ToArray()会在内存中创建对象的副本.如果字节大于80000,则对象将被放置在大对象堆(LOH)中.到目前为止没什么特别的.但是GC不能很好地处理LOH及其中的对象(内存未按预期释放).因为这可能会发生OutOfMemoryException.解决方案是调用GC.Collect()以便收集这些对象或使用GetBuffer()并创建几个较小的(小于80000字节)对象 - 这些对象不会转到LOH并且内存将按预期释放由GC.
存在第三个(更好)选项,即仅使用流,例如从MemoryStream读取所有字节并直接将它们写入HttpResponse.OutputStream(再次使用字节数组<80000字节作为缓冲区).然而,这并不总是可行的(就像我的情况一样).
作为总结,我们可以说当不需要对象的内存中副本时,您将不得不避免使用ToArray(),在这种情况下,GetBuffer()可能会派上用场,但可能不是最佳解决方案.
.NET 4.6有一个新的API,bool MemoryStream.TryGetBuffer(out ArraySegment<byte> buffer)
在精神上类似于.GetBuffer()
.如果可以ArraySegment
,此方法将返回包含_origin
信息的方法.
有关何时返回true并使用有用信息填充out参数的详细信息,请参阅此问题.TryGetBuffer()
.
如果您使用的是低级API ArraySegment
,例如Socket.Send,它会很有用.ToArray
您可以创建一个段,而不是调用哪个将创建该阵列的另一个副本:
var segment=new ArraySegment<byte>(stream.GetBuffer(), 0, stream.Position);
Run Code Online (Sandbox Code Playgroud)
然后将其传递给Send
方法.对于大数据,这将避免分配新阵列并将其复制到其中,这可能很昂贵.
GetBuffer()
始终假设您知道输入字符串的数据结构(这就是它的用途)。如果您想从流中获取数据,您应该始终使用提供的方法之一(例如ToArray()
)。
可以使用类似的东西,但我现在能想到的唯一情况是流中的一些固定结构或虚拟文件系统。例如,在当前位置,您正在读取流内文件的偏移量。然后,您基于该流的缓冲区创建一个新的流对象,但使用不同的_origin
. 这使您无需复制新对象的整个数据,这可能使您节省大量内存。这使您无需随身携带初始缓冲区作为参考,因为您始终能够再次检索它。
GetBuffer
MSDN 文档中最重要的一点,除了它不创建数据的副本之外,是它返回一个包含未使用字节的数组:
请注意,缓冲区包含可能未使用的已分配字节。例如,如果将字符串“test”写入 MemoryStream 对象,则 GetBuffer 返回的缓冲区长度为 256,而不是 4,其中 252 个字节未使用。要仅获取缓冲区中的数据,请使用 ToArray 方法;但是,ToArray 在内存中创建数据的副本。
因此,如果您真的想避免由于内存限制而创建副本,则必须小心不要通过网络发送整个数组GetBuffer
或将其转储到文件或附件中,因为该缓冲区会在任何时候以 2 的幂增长填充并且最后几乎总是有很多未使用的字节。
归档时间: |
|
查看次数: |
17780 次 |
最近记录: |