Использование памяти для сериализации блоков байтов с помощью Protobuf-net
В нашем приложении у нас есть некоторые структуры данных, которые, помимо прочего, содержат сокращенный список байтов (в настоящее время представлен какList<byte[]>
). Мы разбиваем байты на части, потому что если мы позволим помещать байтовые массивы в кучу больших объектов, то со временем мы страдаем от фрагментации памяти.
Мы также начали использовать Protobuf-net для сериализации этих структур, используя нашу собственную сгенерированную DLL сериализации.
Однако мы заметили, что Protobuf-net создает очень большие буферы в памяти при сериализации. Просматривая исходный код, кажется, что, возможно, он не может очистить свой внутренний буфер до полногоList<byte[]>
структура была написана, потому что она должна записать общую длину в начале буфера впоследствии.
Это, к сожалению, отменяет нашу работу с разбивкой байтов в первую очередь и в конечном итоге дает нам OutOfMemoryExceptions из-за фрагментации памяти (исключение возникает в тот момент, когда Protobuf-net пытается расширить буфер до более 84 КБ, что, очевидно, помещает его в LOH, и наше общее использование памяти процесса довольно низко).
Если мой анализ того, как работает Protobuf-net, верен, есть ли способ обойти эту проблему?
Update
Основываясь на ответе Марка, я попробовал вот что:
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase
{
}
[ProtoContract]
public class A : ABase
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public B B
{
get;
set;
}
}
[ProtoContract]
public class B
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<byte[]> Data
{
get;
set;
}
}
Затем сериализовать это:
var a = new A();
var b = new B();
a.B = b;
b.Data = new List<byte[]>
{
Enumerable.Range(0, 1999).Select(v => (byte)v).ToArray(),
Enumerable.Range(2000, 3999).Select(v => (byte)v).ToArray(),
};
var stream = new MemoryStream();
Serializer.Serialize(stream, a);
Однако, если я вставлю точку останова вProtoWriter.WriteBytes()
куда это звонитDemandSpace()
к нижней части метода и шаг вDemandSpace()
Я вижу, что буфер не очищен, потому чтоwriter.flushLock
равняется1
.
Если я создам еще один базовый класс для ABase, как это:
[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}
затемwriter.flushLock
равняется2
вDemandSpace()
.
Я предполагаю, что есть очевидный шаг, который я пропустил здесь, чтобы сделать с производными типами?