ページ 11

C++/CLIのメモリ解放

Posted: 2015年4月15日(水) 17:38
by Referia
現在VC++2013expressでC++/CLIを使用しテトリスを作成しているのですが
画像の描画後にメモリ解放が行われていないのかメモリ使用量が起動時間に比例してどんどん増えていきます
明示的にdeleteキーワードでメモリ解放を行っても一向に改善の余地が見られません。
またDisposeメソッドを呼ぶとコンパイラからメンバに無いとエラーを出されてしまいます
メモリ使用量増加を抑制する方法は何か無いでしょうか?
また、できれば何故Disposeが呼べないのかについても教えていただけると嬉しいです
よろしくお願いします。


コード:

// 描画処理
// 背景等は30×30の画像集合体なので1つにしてから画面に描画
void Tetris::Render(System::Drawing::Graphics^ g, System::Drawing::Size^ size){

	using namespace System;
	using namespace System::Drawing;

	// 画像
	Bitmap^ tetrisImage = gcnew Bitmap("BackGroundImage.png");
	// 合成後の画像
	Bitmap^ tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成用Graphicsクラス
	Graphics^ tetrisGraphic = Graphics::FromImage(tetrisMap);

	// 背景を貼り付け用に描きこむ
	for (int y = 0; y < size->Height; y += 30){
		for (int x = 0; x < size->Width; x += 30){
			tetrisGraphic->DrawImage(tetrisImage, x, y, 30, 30);
		}
	}

	// 合成後の画像をFormに描きこむ
	g->DrawImage(tetrisMap, 0, 0);

	// ボード合成前画像
	tetrisImage = gcnew Bitmap("BoardBackImage.png");
	// ボード合成後画像
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成書き込み用Graohicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	// ColorMatrixオブジェクトの作成
	Imaging::ColorMatrix^ cm = gcnew Imaging::ColorMatrix();

	// ColorMatrixの行列の値を変更して、アルファ値が0.5に変更されるようにする
	cm->Matrix00 = 1;
	cm->Matrix11 = 1;
	cm->Matrix22 = 1;
	cm->Matrix33 = 0.5F;
	cm->Matrix44 = 1;

	// 画像メタデータ格納クラス
	Imaging::ImageAttributes^ ia = gcnew Imaging::ImageAttributes();

	// カラーマトリックスを指定
	ia->SetColorMatrix(cm);

	// 合成用書き込み
	for (int y = 0; y < STAGE_HEIGHT; y++){
		for (int x = 0; x < STAGE_WIDTH; x++){
			tetrisGraphic->DrawImage(tetrisImage, *new Drawing::Rectangle(0, 0, STAGE_WIDTH * 30, STAGE_HEIGHT * 30), System::Int32(x * 30), System::Int32(y * 30), System::Int32(30), System::Int32(30), GraphicsUnit::Pixel, ia);
		}
	}

	// 描きこみ
	g->DrawImage(tetrisMap, 0, 0);
	// ブロック元画像
	tetrisImage = gcnew Bitmap("MoveBlock.png");
	// ブロック画像(Form貼り付け用)
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// ブロック合成用Graphicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	// ブロック切抜き用Rectangleクラス
	Drawing::Rectangle^ rect = gcnew Drawing::Rectangle(0, 0, 29, 29);
	// 各色のブロックを持つBitMap配列
	array<Bitmap^>^ blocks = gcnew array<Bitmap^>(10);

	// ブロックを配列に格納する
	for (int i = 0; i < 7; i++){
		blocks[i] = tetrisImage->Clone(*rect, tetrisImage->PixelFormat);
		rect->Location.X += 30;
		rect->Location.Y += 30;
	}

	// ブロックを貼り付け用に描きこむ
	for (int y = 0; y < STAGE_HEIGHT; y++){
		for (int x = 0; x < STAGE_WIDTH; x++){
			if (board.getBoardStatus(x, y) == Box){
				tetrisGraphic->DrawImage(blocks[block.getMoveBlockType()], x * 30, y * 30, 30, 30);
			}
		}
	}

	// 合成後の画像をFormに描きこむ
	g->DrawImage(tetrisMap, 0, 0);

	// メモリ解放
	delete tetrisGraphic;
	delete tetrisImage;
	delete tetrisMap;
}

Re: C++/CLIのメモリ解放

Posted: 2015年4月15日(水) 19:16
by sleep
Referia さんが書きました:

コード:

// 描画処理
// 背景等は30×30の画像集合体なので1つにしてから画面に描画
void Tetris::Render(System::Drawing::Graphics^ g, System::Drawing::Size^ size){
	// 画像
	Bitmap^ tetrisImage = gcnew Bitmap("BackGroundImage.png");
	// 合成後の画像
	Bitmap^ tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成用Graphicsクラス
	Graphics^ tetrisGraphic = Graphics::FromImage(tetrisMap);

	・・・

	// ボード合成前画像
	tetrisImage = gcnew Bitmap("BoardBackImage.png");
	// ボード合成後画像
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成書き込み用Graohicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	・・・

	// ブロック元画像
	tetrisImage = gcnew Bitmap("MoveBlock.png");
	// ブロック画像(Form貼り付け用)
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// ブロック合成用Graphicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	・・・

	// メモリ解放
	delete tetrisGraphic;
	delete tetrisImage;
	delete tetrisMap;
}
deleteによるデストラクタ(Disposeメソッド)の呼び出しは3回目に確保したリソースにしか適用されていません。
Referia さんが書きました: また、できれば何故Disposeが呼べないのかについても教えていただけると嬉しいです
デストラクターのセマンティクスの変更
デストラクターとファイナライザー

Re: C++/CLIのメモリ解放

Posted: 2015年4月16日(木) 09:03
by Referia
sleepさんありがとうございます。
ごり押しで突破したような気がしないでもないですが、下記のようにgcnewを2回目以降で行う前にdeleteとnullptrの代入を行うとメモリ使用量の増加がかなり改善しました(それでも100KBほどづつ増えていますが・・・)
教えていただいたドキュメントも参考にします、ありがとうございました!

コード:

void Tetris::Render(System::Drawing::Graphics^ g, System::Drawing::Size^ size){

	using namespace System;
	using namespace System::Drawing;

	// 画像
	Bitmap^ tetrisImage = gcnew Bitmap("BackGroundImage.png");
	// 合成後の画像
	Bitmap^ tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成用Graphicsクラス
	Graphics^ tetrisGraphic = Graphics::FromImage(tetrisMap);

	// 書き込み

	// メモリ解放
	delete tetrisGraphic;
	delete tetrisImage;
	delete tetrisMap;
	tetrisGraphic = nullptr;
	tetrisImage = nullptr;
	tetrisMap = nullptr;

	// ボード合成前画像
	tetrisImage = gcnew Bitmap("BoardBackImage.png");
	// ボード合成後画像
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// 合成書き込み用Graohicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	// ColorMatrixオブジェクトの作成
	Imaging::ColorMatrix^ cm = gcnew Imaging::ColorMatrix();

	// ColorMatrixの行列の値を変更して、アルファ値が0.5に変更されるようにする
	cm->Matrix00 = 1;
	cm->Matrix11 = 1;
	cm->Matrix22 = 1;
	cm->Matrix33 = 0.5F;
	cm->Matrix44 = 1;

	// 画像メタデータ格納クラス
	Imaging::ImageAttributes^ ia = gcnew Imaging::ImageAttributes();

	// カラーマトリックスを指定
	ia->SetColorMatrix(cm);

	// 書き込み

	// メモリ解放
	delete tetrisGraphic;
	delete tetrisImage;
	delete tetrisMap;
	delete ia;
	tetrisGraphic = nullptr;
	tetrisImage = nullptr;
	tetrisMap = nullptr;
	ia = nullptr;

	// ブロック元画像
	tetrisImage = gcnew Bitmap("MoveBlock.png");
	// ブロック画像(Form貼り付け用)
	tetrisMap = gcnew Bitmap(size->Width, size->Height);
	// ブロック合成用Graphicsクラス
	tetrisGraphic = Graphics::FromImage(tetrisMap);

	// ブロック切抜き用Rectangleクラス
	Drawing::Rectangle^ rect = gcnew Drawing::Rectangle(0, 0, 29, 29);
	// 各色のブロックを持つBitMap配列
	array<Bitmap^>^ blocks = gcnew array<Bitmap^>(10);

	// 書き込み

	// メモリ解放
	delete tetrisGraphic;
	delete tetrisImage;
	delete tetrisMap;
	delete blocks;
	delete rect;
	tetrisGraphic = nullptr;
	tetrisImage = nullptr;
	tetrisMap = nullptr;
	blocks = nullptr;
	rect = nullptr;
}

Re: C++/CLIのメモリ解放

Posted: 2015年4月16日(木) 10:51
by Blue
Disposeが必要ないものは、deleteをしないほうがベターじゃないですかね?
何のためのガベージコレクションなのってことになりますし。

C++/CLIはgcnew以外にも^を付けないで変数宣言すればインスタンスが出来ますので、
局所変数にすることで、デストラクタを呼ぶことが出来ます。

たとえば

コード:

    // ボード合成前画像
    tetrisImage = gcnew Bitmap("BoardBackImage.png");
    // ボード合成後画像
    tetrisMap = gcnew Bitmap(size->Width, size->Height);
    // 合成書き込み用Graohicsクラス
    tetrisGraphic = Graphics::FromImage(tetrisMap);
 
    // ColorMatrixオブジェクトの作成
    Imaging::ColorMatrix^ cm = gcnew Imaging::ColorMatrix();
 
    // ColorMatrixの行列の値を変更して、アルファ値が0.5に変更されるようにする
    cm->Matrix00 = 1;
    cm->Matrix11 = 1;
    cm->Matrix22 = 1;
    cm->Matrix33 = 0.5F;
    cm->Matrix44 = 1;
 
    // 画像メタデータ格納クラス
    Imaging::ImageAttributes^ ia = gcnew Imaging::ImageAttributes();
 
    // カラーマトリックスを指定
    ia->SetColorMatrix(cm);
 
    // 書き込み
 
    // メモリ解放
    delete tetrisGraphic;
    delete tetrisImage;
    delete tetrisMap;
    delete ia;
    tetrisGraphic = nullptr;
    tetrisImage = nullptr;
    tetrisMap = nullptr;
    ia = nullptr;
は、

コード:

	{
	    // ボード合成前画像
	    Bitmap tetrisImage("BoardBackImage.png");
	    // ボード合成後画像
	    Bitmap tetrisMap(size->Width, size->Height);
	    // 合成書き込み用Graohicsクラス
	    Graphics^ tetrisGraphic = Graphics::FromImage(&tetrisMap); // ここは ^ でしか受けられないので delete する
	 
	    // ColorMatrixオブジェクトの作成
	    Imaging::ColorMatrix cm;
	 
	    // ColorMatrixの行列の値を変更して、アルファ値が0.5に変更されるようにする
	    cm.Matrix00 = 1;
	    cm.Matrix11 = 1;
	    cm.Matrix22 = 1;
	    cm.Matrix33 = 0.5F;
	    cm.Matrix44 = 1;
	 
	    // 画像メタデータ格納クラス
	    Imaging::ImageAttributes ia;
	 
	    // カラーマトリックスを指定
	    ia.SetColorMatrix(cm);
	 
	    // 書き込み
	 
	    // メモリ解放
	    delete tetrisGraphic;
	    tetrisGraphic = nullptr;
	}
というようにかけると思います。(未確認ですが)
# C#のusingのようなイメージで使える。

Re: C++/CLIのメモリ解放

Posted: 2015年4月16日(木) 10:53
by Blue
下のコードの

>>Graphics^ tetrisGraphic = Graphics::FromImage(&tetrisMap); // ここは ^ でしか受けられないので delete する

Graphics^ tetrisGraphic = Graphics::FromImage(%tetrisMap); // ここは ^ でしか受けられないので delete する

>>ia.SetColorMatrix(cm);

ia.SetColorMatrix(%cm);

でした。

Re: C++/CLIのメモリ解放

Posted: 2015年4月17日(金) 09:05
by Referia
Blueさんご丁寧にありがとうございます!
Formの方でgcnewしてインスタンスの生成をしていたのでそのように書くことが普通なのだと勘違いしていました。