質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • C#

    9236questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C#でBitmapをDisposeできずにメモリが増えていくのを対処したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 18K+
退会済みユーザー

退会済みユーザー

C#のBitmapのDisposeについて質問です。

pictureBoxに画像を表示して、Button1を押すと1pixel拡大するというコードを記述しました。

// Button1クリックイベント
private void button1_Click(object sender, EventArgs e) {
    Bitmap img = (Bitmap)pictureBox1.Image;
    pictureBox1.Image = ScaleBmp(img);
}

// 元のBitmapを引き数に渡すと1pixel拡大したBitmapを出力する
private Bitmap ScaleBmp(Bitmap src){
    Bitmap bmp = new Bitmap(src.Width + 1, src.Height + 1);
    using (Graphics g = Graphics.FromImage(bmp)) {
        g.DrawImage(src, 0, 0, src.Width + 1, src.Height + 1);
    }
    return bmp;
}

このButton1を連続で何回も押すとプロセスメモリがどんどん増えていきます。
ScaleBmp関数の中でBitmapを生成してreturnしているので使用メモリが増えるのは当然なのですが巨大な画像の場合はものすごい勢いで増えていきます。

BitmapをpictureBox1.Imageに渡している場合、DisposeするとエラーになるためにDisposeすることができず、メモリが増えてしまいます。

しばらく放置すると.NetFrameworkが勝手にどこかでGCを実行するので不要になったBitmapが解放されるのですが、Button1を連打すると解放される前にメモリリークを起こしそうで困っています。

button1_Click関数の中の最後にGC.Collect()を実行すればいいのかとは思いますが、Button1を連打する度にGCを呼ぶのもよくないと思ってしまいます。

何か対策はありますでしょうか?
どうぞよろしくお願い致します。

Windows10 + VisualStudio2015 C#

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • hihijiji

    2016/09/29 18:55

    pictureBox1.Image = ScaleBmp(img); //の後に
    img.Dispose(); // ←追加
    するだけじゃダメですか?

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2016/09/30 10:36

    ありがとうございます、それで大丈夫でした!

    キャンセル

回答 1

checkベストアンサー

0

これで改善しませんかね?
var oldImage = pictureBox1.Image;
pictureBox1.Image = ScaleBmp(img);
oldImage.Dispose();

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/29 19:18

    ありがとうございます。
    てっきりScaleBmp関数内のBitmap bmp = new Bitmapで生成しまくっているほうがメモリを増やしているとばかり思っていました・・・。
    そちらでメモリは増えなくなりました・・・!

    キャンセル

  • 2016/09/30 10:12

    いや、それで合ってます。メモリの増大分はそれです。
    というか考え方的にも手順的にも概ね合ってます。

    唯一の問題はここで
    >>BitmapをpictureBox1.Imageに渡している場合、DisposeするとエラーになるためにDisposeすることができず、メモリが増えてしまいます。

    これのせいで開放処理ができなくなってしまっていたのが問題です。

    pictureBox1.Image = ScaleBmp(img);
    これだけだと古いBitmapは参照から外れるだけで開放されません。(いつかはGCによって開放されますが)

    キャンセル

  • 2016/09/30 10:35

    再度ありがとうございます。

    var oldImage = pictureBox1.Image;
    pictureBox1.Image = ScaleBmp(img);
    oldImage.Dispose();

    ですが、2行目の引数は

    pictureBox1.Image = ScaleBmp(oldImage);

    ですよね・・・?

    それといまいちわからないのですが、ScaleBmp関数内でnewで生成されたBitmapがどこで開放されているのかわかりません・・・。
    ScaleBmp関数内で生成されたBitmapはpictureBox.Imageに渡りますが、ボタンを再び押すとまた生成されます。つまりその前に生成されたBitmapはどこで開放されているのでしょうか?

    キャンセル

  • 2016/09/30 11:29

    >>pictureBox1.Image = ScaleBmp(oldImage);
    すいません、そうでした。

    >>ScaleBmp関数内で生成されたBitmapはpictureBox.Imageに渡りますが、ボタンを再び押すとまた生成されます。つまりその前に生成されたBitmapはどこで開放されているのでしょうか?

    以前のコードだと一切開放されていません。完全放置状態です。
    そして、それが今回のメモリ増大の原因です。

    例えば、例を挙げると、こんな感じでしょうか

    void Func () {
    Bitmap image1 = (Bitmap)pictureBox1.Image;
    var image2 = ScaleBmp(image1);
    pictureBox1.Image = image2;
    var image3 = ScaleBmp(image2);
    pictureBox1.Image = image3;


    以下繰り返し
    }

    こんな感じでimage1~10000000と処理を増やしていったらどうなるでしょうか?
    当然、すぐにメモリオーバーしますよね?image2も3もそれ以降も全部
    実際にBitmap持ってるわけですから。

    前の処理はまさにこんな感じでした。
    それを今回はこのように直しました。

    void Func () {
    Bitmap image1 = (Bitmap)pictureBox1.Image;
    var image2 = ScaleBmp(image1);
    pictureBox1.Image = image2;
    image1.Dispose(); // ※1
    var image3 = ScaleBmp(image2);
    pictureBox1.Image = image3;
    image2.Dispose(); // 上に同じ


    以下繰り返し
    }

    ※1
    image1.Dispose(); // pictureBox1.Imageはimage2に新しく置き換わってimage1を使わなくなったので、古いイメージであるimage1は開放する



    キャンセル

  • 2016/09/30 13:28

    非常にわかりやすいご説明をありがとうございます!
    とても納得できました!
    Idisposableのあるオブジェクトは開放をきちんとしないとどんどんメモリが増えていきますね・・・。
    今回の件で理解できましたので本当に助かりました。
    ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9236questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。