Недавно я обнаружил очень странную (для меня) утечку памяти для COM-объекта IEnumString , используемого из C #.В частности, вызов метода IEnumString.Next с использованием строкового массива, который уже содержал значения, полученные в результате предыдущего вызова, вызвал утечку памяти.
IEnumString выглядело так на стороне C #:
[InterfaceType(1)]
[Guid("00000101-0000-0000-C000-000000000046")]
public interface IEnumString
{
void Clone(out IEnumString ppenum);
void RemoteNext(int celt, string[] rgelt, out int pceltFetched);
void Reset();
void Skip(int celt);
}
Вызовметод RemoteNext (Next), подобный этому, вызвал утечку, которая была проверена путем многократного запуска его в течение длительного времени и непрерывного роста счетчика «Private Bytes».
string[] item = new string[100]; // OBS! Will be re-used for each call!
for (; ; )
{
int fetched;
enumString.RemoteNext(item.Length, item, out fetched);
if (fetched > 0)
{
for (int i = 0; i < fetched; ++i)
{
// do something with item[i]
}
}
else
{
break;
}
}
Но при создании нового строкового элемента[] массив для каждого вызова как-то заставлял утечку исчезать.
for (; ; )
{
int fetched;
string[] item = new string[100]; // Create a new instance for each call.
enumString.RemoteNext(item.Length, item, out fetched);
if (fetched > 0)
{
for (int i = 0; i < fetched; ++i)
{
// do something with item[i]
}
}
else
{
break;
}
}
Что не так в первом случае?Я предполагаю, что происходит то, что память COM, выделенная для аргумента rgelt IEnumString.Next, который должен быть освобожден вызывающей стороной, как-то не так.
Но второй случай странно работает.
Редактировать : Для получения дополнительной информации, это то, как выглядит "реализация" метода RemoteNext в ILDASM и .NET Reflector.
.method public hidebysig newslot abstract virtual
instance void RemoteNext([in] int32 celt,
[in][out] string[] marshal( lpwstr[ + 0]) rgelt,
[out] int32& pceltFetched) runtime managed internalcall
{
} // end of method IEnumString::RemoteNext
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)]
void RemoteNext(
[In] int celt,
[In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr, SizeParamIndex=0)] string[] rgelt,
out int pceltFetched);
Редактировать 2 : Вы также можете заставить утечку появляться во втором непротекающем случае, просто добавив строковое значение в массив строк (содержащий только нулевые значения) перед вызовом RemoteNext.
string[] item = new string[100]; // Create a new instance for each call.
item[0] = "some string value"; // THIS WILL CAUSE A LEAK
enumString.RemoteNext(item.Length, item, out fetched);
Так что кажется, что элемент массивадолжен быть пустым для слоя маршалинга, чтобы правильно освобождать неуправляемые строки, скопированные на него.Даже в этом случае массив будет возвращать те же значения, т. Е. Наличие непустого массива не приводит к возвращению неправильных строковых значений, а просто приводит к утечке некоторых.