En nuestra aplicación tenemos algunas estructuras de datos que, entre otras cosas, contienen una lista fragmentada de bytes (actualmente expuesta como List<byte[]>
). Recopilamos los bytes porque si permitimos que las matrices de bytes se coloquen en el gran montón de objetos, con el tiempo sufriremos fragmentación de la memoria.Uso de memoria serialización de matrices de bytes fragmentados con Protobuf-net
También hemos comenzado a usar Protobuf-net para serializar estas estructuras, utilizando nuestra propia DLL de serialización generada.
Sin embargo, hemos notado que Protobuf-net crea buffers en memoria muy grandes durante la serialización. Echando un vistazo a través del código fuente, parece que quizás no pueda vaciar su búfer interno hasta que se haya escrito la totalidad de la estructura List<byte[]>
porque después necesita escribir la longitud total en la parte delantera del búfer.
Desafortunadamente, deshacemos nuestro trabajo de dividir los bytes en primer lugar, y finalmente nos da OutOfMemoryExceptions debido a la fragmentación de la memoria (la excepción ocurre en el momento en que Protobuf-net intenta expandir el buffer a más de 84k, lo que obviamente lo pone en LOH, y nuestro uso general de la memoria de proceso es bastante bajo).
Si mi análisis de cómo funciona Protobuf-net es correcto, ¿hay alguna forma de solucionar este problema?
actualización
Sobre la base de 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;
}
}
Luego de 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 quedo un punto de interrupción en ProtoWriter.WriteBytes()
donde llama DemandSpace()
hacia la parte inferior del método y el paso en DemandSpace()
, puedo ver que el búfer no se vacía porque writer.flushLock
es igual a 1
.
Si creo otra clase base para Abase así:
[ProtoContract]
[ProtoInclude(1, typeof(ABase), DataFormat = DataFormat.Group)]
public class ABaseBase
{
}
[ProtoContract]
[ProtoInclude(1, typeof(A), DataFormat = DataFormat.Group)]
public class ABase : ABaseBase
{
}
Entonces writer.flushLock
es igual a 2
en DemandSpace()
.
Supongo que hay un paso obvio que he perdido aquí para hacer con los tipos derivados?
Gracias por la respuesta rápida. Su suposición acerca de nuestra estructura de datos era correcta. ¿Tendría razón al decir que necesitamos cambiar el DataFormat a Group para cualquier propiedad que contenga una referencia a A, y así sucesivamente hasta la raíz del gráfico de objetos? ¿Y este cambio también debería estar en los atributos relevantes de ProtoInclude también? –
@Juegos esencialmente, sí. Hmmm ... ¡Tal vez debería agregar un valor predeterminado de modelo para eso! –
He actualizado mi pregunta con mi intento de utilizar DataFormat.Group para resolver el problema, pero sigo teniendo problemas para que el buffer se descargue. Disculpas si soy un idiota ... –