Исключение System.OutOfMemory при вызове MemoryStream.ToArray () после сериализации объекта с помощью DataContractSerializer - PullRequest
0 голосов
/ 15 июля 2011

Я получаю прерывистое исключение «нехватка памяти» в этом операторе:

        return ms.ToArray();

В этом методе:

public static byte[] Serialize(Object inst)
{
    Type t = inst.GetType();
    DataContractSerializer dcs = new DataContractSerializer(t);
    MemoryStream ms = new MemoryStream();
    dcs.WriteObject(ms, inst);
    return ms.ToArray();
}

Как я могу предотвратить это? Есть ли лучший способ сделать это?

Длина ms составляет 182 870 206 байт (174,4 МБ)

Я помещаю это в байтовый массив, чтобы потом можно было запустить его через сжатие и сохранить на диске. Эти данные (очевидно) представляют собой большой список пользовательских классов, которые я загружаю с сервера WCF при запуске приложения Silverlight. Я сериализую его и сжимаю, чтобы он использовал только около 6 МБ в изолированном хранилище. В следующий раз, когда пользователь посещает и запускает приложение silverlight из Интернета, я проверяю метку времени, и если это хорошо, я просто открываю файл из изолированного файла, распаковываю его, десериализую его и загружаю свою структуру. Я храню всю структуру в памяти, потому что приложение в основном ориентировано на манипулирование содержимым этой структуры.

@ Конфигуратор правильный. Размер массива был слишком велик. Я прокрутил собственный сериализатор, объявив байтовый массив [количество записей в списке * число байтов на запись], а затем сам наполнил его, используя такие выражения, как это:

    Buffer.BlockCopy(
           BitConverter.GetBytes(node.myInt),0,destinationArray,offset,sizeof(int)); 
    offset += sizeof(int);

и это, чтобы получить его обратно:

    newNode.myInt= BitConverter.ToInt32(sourceByteArray,offset); 
    offset += sizeof(int);

Затем я сжал его и сохранил в изолированном хранилище.

Мой размер увеличился с 174 МБ с DataContractSerializer до 14 МБ с моим. После сжатия он перешел с 6 МБ до 1 МБ файла в изолированном хранилище.

Спасибо Конфигуратору и Филипу за помощь.

Ответы [ 4 ]

3 голосов
/ 15 июля 2011

Кажется, проблема в том, что вы ожидаете возврата массива байтов 180 МБ. Это означает, что фреймворк должен будет находить и выделять последовательные 180 МБ свободной памяти для копирования потоковых данных, что обычно довольно сложно - отсюда и исключение OutOfMemoryException. Если вам нужно продолжать обрабатывать этот объем памяти, используйте сам поток памяти (для чтения и записи в него, как вам нужно) для хранения буфера; в противном случае сохраните его в файл (или в любое другое место, в котором он вам нужен, например, для передачи по сети) напрямую, вместо использования потока памяти.

Я должен отметить, что поток памяти имеет собственный массив размером 180 МБ, поэтому он также имеет некоторые проблемы и может вызвать OutOfMemory во время сериализации - он, вероятно, будет лучше (например, более устойчивым), если Вы можете сериализовать его во временный файл . Возможно, вы также захотите рассмотреть более компактный - но, возможно, менее читаемый - формат сериализации, такой как json, двоичная сериализация или буферы протокола.


В ответ на комментарий: для сериализации непосредственно на диск используйте FileStream вместо MemoryStream:

public static void Serialize(Object inst, string filename)
{
    Type t = inst.GetType();
    DataContractSerializer dcs = new DataContractSerializer(t);
    using (FileStream stream = File.OpenWrite(filename)) {
        dcs.WriteObject(ms, inst);
    }
}
0 голосов
/ 24 октября 2016

Попробуйте сериализовать в поток (т.е. FileStream) вместо байтового массива.Таким образом, вы можете сериализовать гигабайты данных без исключения OutOfMemory.

        public static void Serialize<T>(T obj, string path)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            Stream stream = File.OpenWrite(path);
            serializer.WriteObject(stream, obj);
        }

        public static T Deserialize<T>(string path)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            Stream stream = File.OpenRead(path);
            return (T)serializer.ReadObject(stream);
        }
0 голосов
/ 15 июля 2011

Я не знаю, как вы используете этот код, но меня поражает то, что вы не высвобождаете свои ресурсы.Например, если вы будете вызывать Serialize(obj) много раз с большим количеством больших объектов, у вас будет много используемой памяти, которая не высвобождается напрямую, однако GC должен справиться с этим правильно, но вы должнывсегда высвобождайте свои ресурсы.

Я пробовал этот фрагмент кода:

public static byte[] Serialize(object obj)
{
    Type type = obj.GetType();
    DataContractSerializer dcs = new DataContractSerializer(type);

    using (var stream = new MemoryStream())
    {
        dcs.WriteObject(stream, obj);
        return stream.ToArray();
    }
}

со следующим Main -методом в консольном приложении

static void Main(string[] args)
{
    var filipEkberg = new Person {Age = 24, Name = @"Filip Ekberg"};

    var obj = Serialize(filipEkberg);
}

Однако мой byte массив не такой большой, как ваш. Посмотрев на подобную проблему , вы можете рассмотреть возможность проверки protobuf-net .

Также может быть интересно узнать, что вы собираетесь делатьс сериализованными данными, нужен ли он как byte-массив или это может быть XML-файл, записанный в текстовый файл?

0 голосов
/ 15 июля 2011

Попробуйте установить позицию потока памяти в 0 и только после вызова ToArray ().

Привет.

...