私たちのアプリケーションでは、とりわけチャンクされたバイトのリスト(現在はList<byte[]>
として公開されています)が含まれています。バイト配列を大きなオブジェクトのヒープに置くことができれば、時間がたつにつれメモリの断片化に悩まされるので、バイトをチャンクアップします。Protobuf-netを使用したチャンク付きバイト配列のメモリ使用のシリアライズ
また、Protobuf-netを使用して、独自の生成されたシリアル化DLLを使用してこれらの構造をシリアル化しています。
しかし、私たちは、Protobuf-netがシリアル化中に非常に大きなインメモリバッファを作成していることに気付きました。ソースコードを見てみると、List<byte[]>
構造体全体が書き込まれるまで内部バッファをフラッシュすることができないかもしれないと思われます。なぜならバッファの後ろに合計長さを書き込む必要があるからです。
これは残念なことに、最初にバイトをチャンクすることで私たちの仕事を元に戻し、メモリの断片化のためにOutOfMemoryExceptionsを与えます(Protobuf-netがバッファを84k以上に拡張しようとしているときに例外が発生します。それをLOHに置き、プロセス全体のメモリ使用量はかなり低い)。
Protobuf-netがどのように機能しているかを分析したところ、この問題を回避する方法はありますか?
更新
マルクの答えに基づいて、ここで私が試したものです:
[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
はDemandSpace()
で2
に等しいです。
派生型とここでは分かりませんでした。
クイック返信をありがとう。私たちのデータ構造についてのあなたの推測は正しいものでした。 Aへの参照を含むプロパティについても、DataFormatをGroupに変更し、オブジェクトグラフのルートまで同様に変更する必要があると言うのは間違いありませんか?また、この変更は関連するProtoInclude属性にも必要ですか? –
@Jamesは本質的にはいです。うーん...おそらく、私はモデルレベルのデフォルトを追加するべきです! –
DataFormat.Groupを使用して問題を解決しようとしたところで私の質問を更新しましたが、まだバッファをフラッシュするのに問題があります。私がばかだと謝罪.. –