0

ビットマップを取得し、GZipStream を使用して圧縮し、すべてメモリ内でソケット経由で送信するアプリケーションがあります。次の行までのダーティ スカムバッグ メモリ リークを突き止めました。

frame.Save(inStream, jpegCodec, parameters);

古き良き情報スーパーハイウェイをブラウズしていると、さまざまなコーデックの save メソッドで Image クラスがメモリをリークしているというトピックを多数見つけました。問題は、私が見つけることができる修正が実際には存在しないことです。だから私の質問は次のとおりです:

  1. これの原因は何ですか
  2. どうすればこれを修正できますか

リークが見つかった FrameStream クラスの完全な Write() メソッドを次に示します。

/// <summary>
    /// Writes a frame to the stream
    /// </summary>
    /// <param name="frame">The frame to write</param>
    public void Write(Bitmap frame) {
        using (EncoderParameter qualityParameter = new EncoderParameter(Encoder.Quality, 50L)) {
            using (EncoderParameters parameters = new EncoderParameters(1)) {
                parameters.Param[0] = qualityParameter;

                ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
                ImageCodecInfo jpegCodec = null;

                foreach (ImageCodecInfo codec in codecs) {
                    if (codec.MimeType == "image/jpeg") {
                        jpegCodec = codec;
                        break;
                    }
                }

                using (MemoryStream inStream = new MemoryStream()) {
                    frame.Save(inStream, jpegCodec, parameters); // HUUUGE Memory Leak
                    Byte[] buffer = new Byte[inStream.Length];
                    inStream.Read(buffer, 0, buffer.Length);

                    using (MemoryStream outStream = new MemoryStream()) {
                        using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress)) {
                            gzipStream.Write(buffer, 0, buffer.Length);
                        }

                        Byte[] frameData = outStream.ToArray();
                        Byte[] packet = new Byte[15 + frameData.Length];
                        Byte[] frameLength = BitConverter.GetBytes(frameData.Length);

                        Array.Copy(frameLength, 0, packet, 0, frameLength.Length);
                        Array.Copy(frameData, 0, packet, 15, frameData.Length);

                        m_Socket.Send(packet);
                    }
                }
            }
        }
    }
4

5 に答える 5

4

CLR Profiler でコードを実行して、リークの原因を特定することをお勧めします。それが任意の型のマネージド オブジェクト (アンマネージド リソースであっても) である場合、マネージド型がアンマネージド ハンドルをリークしていることがバグの原因でない限り、リークの場所を確認できます。フレームワーク コード内にある場合は、おそらくリフレクションと P/Invoke を使用して回避できます。

たとえば、この型は状況によってはIconWin32 をリークします。HICONこれを回避するには、によって公開されたハンドルを使用して、関数をHICONPInvoking することにより、 を手動で破棄します。DeleteObjectIcon

于 2009-05-16T05:28:05.257 に答える
3

さて、みんなのアイデアや考えや他の多くの方法を試した後。私はついに簡単なことを試しました:

using (frame) {
    frame.Save(outStream, jpegCodec, parameters);
}

これは機能し、メモリリークは修正されました。ガベージコレクターを強制的に呼び出し、ビットマップを手動で破棄し、P / Invoke DeleteObjectを使用してみましたが、何も機能しませんでしたが、usingステートメントを使用すると機能しました。だから、これは私が見逃しているusingステートメントで内部で何が起こるのか疑問に思います...

于 2009-05-16T06:22:55.733 に答える
2

ビットマップの処理が終了したら、破棄後にビットマップを null に設定する必要があります。

また、ビットマップの破棄後にガベージ コレクターを呼び出すこともできます (コストのかかる操作ではありますが)。GC.Collect();

ビットマップは管理されていないリソースを保持します - GC は常にこれらと「ボールに乗っている」とは限りません。Bitmap クラスに関する興味深いリンク (コンパクト フレームワークの観点から) は次のとおりです

于 2009-05-16T05:25:09.227 に答える
0

ソケットについてはよくわかりません。ただし、Image クラスでのメモリ リークを防ぐ 1 つの方法は、ビットマップをフリーズすることです。うまくいけば、この投稿でさらに多くの情報が得られるかもしれません。MemoryStream.Dispose が失敗する可能性はありますか? これにより、メモリリークが発生します。

于 2009-05-16T03:43:44.743 に答える
0

Graphics オブジェクトの .Dispose() メソッドを呼び出していますか? それはメモリリークを引き起こします。編集: byte[] を記述したら、Bitmap オブジェクトの .Dispose() を明確にします。

于 2009-05-16T03:44:52.197 に答える