Managed Heap Object pinned
引自
OutOfMemoryException and Pinning
http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx
如果大量使用锁定的object,managed heap会被不可移动的内存块分割
成很多的小碎片,导致OutOfMemoryException.
比如Socket.BeginReceive的buffer参数就会被锁定, 以便unmanaged code 可以
访问这个buffer.
使用SOS可以发现
0:000>!dumpheap
Address MT Size
00a71000 0015cde8 12 Free
00a7100c 0015cde8 12 Free
00a71018 0015cde8 12 Free
00a71024 5ba58328 68
00a71068 5ba58380 68
00a710ac 5ba58430 68
00a710f0 5ba5dba4 68
…
00a91000 5ba88bd8 2064
00a91810 0019fe48 2032 Free
00a92000 5ba88bd8 4096
00a93000 0019fe48 8192 Free
00a95000 5ba88bd8 4096
…
total 1892 objects
Statistics:
MT Count TotalSize Class Name
5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
…
0015cde8 6 10260 Free
5ba57bf8 318 18136 System.String
…
以上数据表明在heap中有3个free slots
statistics一节显示heap中有10,260 bytes的Free objects和18,136 bytes 的字符串.
之所以可以看到这些free object,就说明这些free ojbect处在无法回收的live object之间.
如果heap中存在大量的Free objects,就表明heap中存在大量的内存碎片.
进一步可以使用
0:000>!dumpobj 00a92000 //00a92000 处的对象是一个byte array
Name: System.Byte[]
MethodTable 0x00992c3c
EEClass 0x00992bc4
Size 4096(0x1000) bytes
Array: Rank 1, Type System.Byte
Element Type: System.Byte
0:000>!gcroot 00a92000 //address 00a92000 it’s rooted by local variables in thread 1
Scan Thread 0 (728)
Scan Thread 1 (730)
ESP:88cf548:Root:05066b48(System.IO.MemoryStream)->00a92000 (System.Byte[])
ESP:88cf568:Root:05066b48(System.IO.MemoryStream)->00a92000 (System.Byte[])
…
Scan HandleTable 9b130
Scan HandleTable 9ff18
HANDLE(Pinned):d41250:Root: 00a92000 (System.Byte[]) //pinned handle.
0:000>!objsize //显示所有pinned 对象的handle
…
HANDLE(Pinned):d41250: sizeof(00a92000) = 4096 ( 0x1000) bytes (System.Byte[])
HANDLE(Pinned):d41254: sizeof(00a95000) = 4096 ( 0x1000) bytes (System.Byte[])
HANDLE(Pinned):d41258: sizeof(00ac8b5b0) = 16 ( 0x10) bytes (System.Byte[])
…
查看对象的详细信息,
可以采用以下措施来减小锁定 object的负面影响:
1.尽量使两个相邻的锁定 object挨得更紧.
2.尽量使锁定 object处在heap的底部,这是由于free的内存集中在heap的顶部.
3.缩短object被锁定的时间.
4.不要每次create一个对象,然后锁定它,而是重用被锁定的对象.
以下的代码演示如何使用一个1k的被锁定的buffer:
public class BufferPool
{
private const int INITIAL_POOL_SIZE = 512; // initial size of the pool
private const int BUFFER_SIZE = 1024; // size of the buffers
// pool of buffers
private Queue m_FreeBuffers;
// singleton instance
private static BufferPool m_Instance = new BufferPool();
public static BufferPool Instance
{
get
{
return m_Instance;
}
}
protected BufferPool()
{
m_FreeBuffers = new Queue(INITIAL_POOL_SIZE);
for (int i = 0; i < INITIAL_POOL_SIZE; i++)
{
m_FreeBuffers.Enqueue(new byte[BUFFER_SIZE]);
}
}
// check out a buffer
public byte[] Checkout(uint size)
{
if (m_FreeBuffers.Count > 0)
{
lock (m_FreeBuffers)
{
if (m_FreeBuffers.Count > 0)
return (byte[])m_FreeBuffers.Dequeue();
}
}
// instead of creating new buffer,
// blocking waiting or refusing request may be better
return new byte[BUFFER_SIZE];
}
// check in a buffer
public void Checkin(byte[] buffer)
{
lock (m_FreeBuffers)
{
m_FreeBuffers.Enqueue(buffer);
}
}
}
