Wykorzystanie pamięci szeregowanie podzielonych tablic bajtów za pomocą protokołu Protobuf-net
W naszej aplikacji mamy pewne struktury danych, które między innymi zawierają podzieloną listę bajtów (obecnie wyświetlaną jakoList<byte[]>
). Rozdzielamy bajty, ponieważ jeśli pozwolimy na umieszczenie tablic bajtów na stercie dużych obiektów, to z czasem cierpimy na fragmentację pamięci.
Zaczęliśmy również używać Protobuf-net do serializacji tych struktur, używając naszego własnego generowanego DLL serializacji.
Zauważyliśmy jednak, że Protobuf-net tworzy bardzo duże bufory w pamięci podczas serializacji. Spoglądając przez kod źródłowy, wydaje się, że być może nie jest w stanie opróżnić wewnętrznego bufora do końcaList<byte[]>
struktura została napisana, ponieważ później musi zapisać całkowitą długość z przodu bufora.
To niestety unieważnia naszą pracę z dzieleniem bajtów w pierwszej kolejności i ostatecznie daje nam OutOfMemoryExceptions z powodu fragmentacji pamięci (wyjątek występuje w momencie, gdy Protobuf-net próbuje rozszerzyć bufor do ponad 84k, co oczywiście umieszcza go na LOH, a nasze ogólne wykorzystanie pamięci procesowej jest dość niskie).
Jeśli moja analiza działania Protobuf-net jest poprawna, czy istnieje sposób na rozwiązanie tego problemu?
Aktualizacja
Na podstawie odpowiedzi Marca oto, co próbowałem:
[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;
}
}
Następnie, aby go serializować:
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);
Jeśli jednak włożę punkt przerwaniaProtoWriter.WriteBytes()
gdzie dzwoniDemandSpace()
w kierunku dolnej części metody i wkrocz do niejDemandSpace()
, Widzę, że bufor nie jest opróżniany, ponieważwriter.flushLock
równa się1
.
Jeśli utworzę inną klasę bazową dla ABase w ten sposób:
[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}
Następniewriter.flushLock
równa się2
wDemandSpace()
.
Zgaduję, że istnieje oczywisty krok, którego brakowało mi tutaj w przypadku typów pochodnych?