Uso de memoria mediante la serialización de matrices de bytes fragmentados con Protobuf-net

En nuestra aplicación tenemos algunas estructuras de datos que, entre otras cosas, contienen una lista de bytes fragmentada (actualmente expuesta comoList<byte[]>). Aumentamos los bytes porque si permitimos que las matrices de bytes se coloquen en el montón de objetos grandes, con el tiempo sufrimos la fragmentación de la memoria.

También hemos empezado a usar Protobuf-net para serializar estas estructuras, utilizando nuestra propia DLL de serialización generada.

Sin embargo, hemos notado que Protobuf-net está creando búferes de memoria muy grandes al serializar. Echando un vistazo a través del código fuente, parece que tal vez no pueda vaciar su búfer interno hasta que todo elList<byte[]> La estructura se ha escrito porque necesita escribir la longitud total en la parte frontal del búfer posteriormente.

Desafortunadamente, esto deshace nuestro trabajo con la fragmentación de los bytes en primer lugar y, finalmente, nos da OutOfMemoryExceptions debido a la fragmentación de la memoria (la excepción se produce en el momento en que Protobuf-net intenta expandir el búfer a más de 84k, lo que obviamente lo coloca en el LOH, y nuestro uso de memoria de proceso general es bastante bajo).

Si mi análisis de cómo funciona Protobuf-net es correcto, ¿hay alguna forma de solucionar este problema?

Actualizar

Basado en la respuesta de Marc, esto es lo que he intentado:

[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;
    }
}

Entonces para serializarlo:

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);

Sin embargo si me pongo un punto de interrupción enProtoWriter.WriteBytes() donde llamaDemandSpace() hacia la parte inferior del método y paso enDemandSpace(), Puedo ver que el búfer no se está vaciando porquewriter.flushLock es igual a1.

Si creo otra clase base para ABase como esta:

[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}

[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}

Entonceswriter.flushLock es igual a2 enDemandSpace().

¿Supongo que hay un paso obvio que he omitido aquí para hacer con los tipos derivados?

Respuestas a la pregunta(2)

Su respuesta a la pregunta