I've found and fixed a Memory Leak which occures with these pinned byte arrays during reading.
In our case, with each ExpatReader instance there were 16MB left after calling Dispose(),
The Fix: class Kds.Xml.Expat.TextBufferReader / StreamBufferedReader in ExpatHelper.cs (Project: ExpatInterop)
1) When stream reads 0 bytes, the method should return an EmptyHandle and 0. Otherwise the calling ExpatUtils.Parse() will not Free() the GCHandle, correctly, because it uses the read bytes as an indicator if the GCHandle is pinned.
2) I had to change how to deal with empty Handles (ExpatUtils.EmptyHandle)
from GCHandle EmptyHandle = GCHandle.Alloc(null);
to GCHandle EmptyHandle = GCHandle.Alloc(new byte[0], GCHandleType.Pinned);
--------
Attached File: ExpatHelpers.cs
[..]
public static readonly GCHandle EmptyHandle = GCHandle.Alloc(new byte[0], GCHandleType.Pinned);
[...]
Method: public int Read(int count, out GCHandle bufferHandle)
public int Read(int count, out GCHandle bufferHandle)
{
if (count == 0) {
bufferHandle = ExpatUtils.EmptyHandle;
return 0;
}
if (byteBuffer.Length < count)
Array.Resize<byte>(ref byteBuffer, count);
int result = stream.Read(byteBuffer, 0, count);
//Bugfix: When stream reads 0 bytes, return an EmptyHandle.
if (result == 0)
{
bufferHandle = ExpatUtils.EmptyHandle;
return 0;
}
bufferHandle = GCHandle.Alloc(byteBuffer, GCHandleType.Pinned);
return result;
}