OutOfMemoryError вызывает XmlSerializer.Deserialize () - не связано с размером XML! - PullRequest
8 голосов
/ 13 мая 2010

Это действительно сумасшедшая ошибка. Ниже выдается OutOfMemoryException для очень коротких и простых фрагментов XML (например, <ABC def='123'/>):

public static T DeserializeXmlNode<T>(XmlNode node)
{
    try
    {
        return (T)new XmlSerializer(typeof(T))
            .Deserialize(new XmlNodeReader(node));
    }
    catch (Exception ex)
    {
        throw; // just for catching a breakpoint.
    }
}

Я прочитал в этой статье MSDN , что если бы я использовал XmlSerializer с дополнительными параметрами в конструкторе, я бы в конечном итоге генерировал не кэшированные сборки сериализатора при каждом вызове, вызывая Утечка в сборке . Но я не использую дополнительные параметры в конструкторе. Это также происходит в первый раз, когда он вызывается в только что запущенном AppDomain, так что это тоже не имеет смысла.

Что дает?

Ответы [ 4 ]

4 голосов
/ 17 февраля 2011

Ну, окончательный ответ на мой вопрос не поможет всем, кто сталкивался с этим, но некоторые из моих коллег также столкнулись в этом месяце спустя в другой системе с другим продуктом. Смеялись, когда они нашли мой пост здесь SO несколько месяцев спустя, и удивлялись, решил я это на самом деле или нет, поскольку здесь не было принято никакого решения.

Окончательное решение не имеет ничего общего с проблемами десериализации. Вместо этого он включал в себя полное удаление и установку новой копии клиента базы данных Oracle ODP.NET , который используют многие, если не все наши приложения.

Судя по неподтвержденным данным, эта проблема возникает в неправильно исправленных версиях сборок ODP.NET, которые впоследствии были распространены на другие системы через клоны виртуальных машин.

Когда ODP.NET был полностью удален, а с веб-сайта Oracle установлена ​​и установлена ​​новая совместимая версия, проблема полностью исчезла.

Гипотеза заключается в том, что используемый (но поврежденный) драйвер ODP.NET имеет небезопасный код и неоднократно перезаписывал защищенную область памяти .NET рядом с методом Deserialize после первого использования. Если бы Deserialize вызывался до каких-либо вызовов ODP.NET, он бы работал просто отлично. Однако все последующие вызовы для десериализации после использования любых вызовов ODP.NET с треском провалились.

Окончательное решение, которое теперь разрешено дважды в двух разных продуктах, - это установить хорошую / свежую / чистую / новую копию ODP.NET.

Не красиво ... но вот что это решило.

2 голосов
/ 17 мая 2010

РЕДАКТИРОВАТЬ: К сожалению, это не было решением, но оно может помочь другим найти очень похожую проблему. Этот ответ здесь является актуальным решением.

Я верю Я нашел решение этой проблемы. Это ошибка в .NET 3.5 SP1 .

Сериализация зависает или создает исключение OutOfMemoryException со статическим делегатом и ISerializable на 3.5 SP1 (ID: 361615 ):

Когда универсальный класс реализует ISerializable и имеет статический член-делегат, который использует аргументы универсального типа, двоичная десериализация зависает (в 32-битной системе с Windows Server 2003) или генерирует исключение OutOfMemoryException (в 64 система с Windows Server 2008).

Эта ошибка возникает с .NET 3.5 SP1 и не возникает с .NET 3.5 без SP1.

Решением является установка KB957543 оперативного исправления.

1 голос
/ 13 мая 2010

На основании документации класса XmlSerializer вы должны кэшировать XmlSerializer, иначе вы можете вызвать низкую производительность или утечку памяти.

Hashtable serializers = new Hashtable();

// Use the constructor that takes a type and XmlRootAttribute.
XmlSerializer s = new XmlSerializer(typeof(MyClass), myRoot);

// Implement a method named GenerateKey that creates unique keys 
// for each instance of the XmlSerializer. The code should take 
// into account all parameters passed to the XmlSerializer 
// constructor.
object key = GenerateKey(typeof(MyClass), myRoot);

// Check the local cache for a matching serializer.
XmlSerializer ser = (XmlSerializer)serializers[key];
if (ser == null) 
{
    ser = new XmlSerializer(typeof(MyClass), myRoot);
    // Cache the serializer.
    serializers[key] = ser;
}
else
{
    // Use the serializer to serialize, or deserialize.
}
1 голос
/ 13 мая 2010

Вы не предоставили достаточно информации, чтобы воссоздать вашу проблему. Но, читатель реализует IDisposable и должен быть утилизирован должным образом. Желательно, завернув его в блок использования. Большинство разработчиков никогда не сталкиваются с проблемой, когда забывают что-то утилизировать, потому что сборщик мусора в конечном итоге уберет беспорядок. Тем не менее, нетрудно кодировать что-то, что вызывает проблемы до того, как GC приступает к очистке, или даже полностью предотвращает очистку.

public static T DeserializeXmlNode<T>(XmlNode node)
{
    XmlSerializer xs = new XmlSerializer(typeof(T));
    using(XmlNodeReader xr = new XmlNodeReader(node))
        return (T)xs.Deserialize(xr);
}
...