본문 바로가기

프로그래밍/Delphi

Graphics32에서 TBitmap32를 Thread 내에서 사용했을때의 문제점 추정

스레드 내에서 Graphics32에서 TBitmap32와 TJpegImage를 사용하여 Jpeg이미지를 로딩 하고
TBitmap32 객체를 해제 했을 경우에 메모리 릭(Leak)이 발생하는 것 처럼 보인다.
이것은 TBitmap32문제라기 보다는 아마도 TJpegImage가 스레드에 안전하지 않은 것으로 추정된다.

테스트 프로그램을 만들어 비스레드 모드에서 이미지 로딩후 해제 하는 부분과
스레드 모드에서 로딩후 해제 하는 부분을 만들어 테스트 했을 때 메모리가 해제되지 않고 계속 남아 있는 것을 확인할 수 있는에 이 메모리 릭은 유레카 로그에도 검출되지 않는다..


unit uMain;
{**
  JPEGImage를 로딩하고 해제할때 메모리가 정확하게 반환이 되는지 체크
  하기 위한 용도임

  작성자 : 온달
  작성일 : 2011.07.23
  아이서퍼3 내부에서 기사 Draw부분에서 로컬 파일을 읽어서 보여주고 해제 하는데
  메모리가 감소하지 않는 문제 처리 테스트 - 스레드와 비스레드내에서...
  }
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, GR32, jpeg, StdCtrls, ExtDlgs, GR32_Image, ExtCtrls;

type
  TForm1 = class(TForm)
    OpenPictureDialog1: TOpenPictureDialog;
    Button1: TButton;
    Button2: TButton;
    Panel1: TPanel;
    iv: TImgView32;
    Memo1: TMemo;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TGThread = class(TThread)
  public
    FBitmap : TBitmap32;
    procedure Draw; // SyncFunction
    procedure Execute; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{** 지정한 파일로부터 이미지를 로딩합니다. ( 지원하는 포맷 : JPEG, PNG, BMP, GIF )
  @param ABitmap32 로딩한 이미지를
  @param
  @result 이미지 로딩이 성공하면 true, 실패하면 false 반환
  *}
function LoadBitmap32FromImageStream(ABitmap32: TBitmap32; AStream: TStream): boolean;
var
  AJpegImage: TJpegImage;
  ABitmap: TBitmap;
begin
  Result := false;
  if (0 = AStream.Size) then
    exit;

  ABitmap := TBitmap.Create;
  try
    try
      AJpegImage := TJpegImage.Create;
      AStream.Seek(0, 0);
      AJpegImage.LoadFromStream(AStream);
      ABitmap.Assign(AJpegImage);
      ABitmap32.Assign(ABitmap);
    finally
      AJpegImage.Free;
    end;
    Result := true;
  except //modified by niceondal, 2011.07.23, 원래는 finally로 되었음
    Result := false;
  end;
  ABitmap.Free;
end;


{** 지정한 파일로부터 이미지를 로딩합니다. ( 지원하는 포맷 : JPEG, PNG, BMP, GIF )
  @param ABitmap32 로딩한 이미지를
  @param
  @result 이미지 로딩이 성공하면 true, 실패하면 false 반환
  *}
function LoadBitmap32FromImageFile(ABitmap32: TBitmap32; const strFileName: string): boolean;
var
  AMemoryStream: TMemoryStream;
begin
  Result := false;
  if (not FileExists(strFileName)) then
    exit;

  AMemoryStream := TMemoryStream.Create;
  try
    AMemoryStream.LoadFromFile(strFileName);
    Result := LoadBitmap32FromImageStream(ABitmap32, AMemoryStream);
  finally
    AMemoryStream.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ABitmap32 : TBitmap32;
begin
  ABitmap32 := TBitmap32.Create;
  LoadBitmap32FromImageFile(ABitmap32, 'C:\Temp\3e4e32aa141be3e2345b.jpg');
  ABitmap32.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ABitmap32 : TBitmap32;
  jpeg : TJpegImage;
  bmp : TBitmap;
begin
  ABitmap32 := TBitmap32.Create;
  jpeg := TJpegImage.Create;
  bmp := TBitmap.Create;
  jpeg.LoadFromFile('C:\Temp\3e4e32aa141be3e2345b.jpg');
  bmp.Assign(jpeg);
  ABitmap32.Assign(bmp);
  bmp.free;
  jpeg.free;
  ABitmap32.Free;
end;

{ TGThread }

procedure TGThread.Draw;
begin
  form1.iv.Bitmap.Assign(FBitmap);
  form1.iv.Refresh;
  sleep(200);
end;

procedure TGThread.Execute;
var
  i : integer;
  ABitmap : TBitmap32;
begin
  for i := 0 to form1.memo1.Lines.count-1 do
  begin
    ABitmap := TBitmap32.Create;
    LoadBitmap32FromImageFile(ABitmap,  form1.memo1.lines[i]);
    FBitmap := ABitmap;
    Synchronize(Draw);
    ABitmap.Free;
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  i : integer;
  ABitmap : TBitmap32;
begin
  for i := 0 to memo1.Lines.count-1 do
  begin
    ABitmap := TBitmap32.Create;
    LoadBitmap32FromImageFile(ABitmap, memo1.lines[i]);
    iv.Bitmap.Assign(ABitmap);
    iv.Refresh;
    sleep(100);
    ABitmap.Free;
  end;

end;

procedure TForm1.Button4Click(Sender: TObject);
var
  th : TGThread;
begin
  th := TGThread.Create(false);
  th.Resume;
end;

end.

[참조] http://graphics32.org/news/newsgroups.php?art_group=graphics32.general&article_id=9835