(200+)帮忙分析一下内存泄漏问题!!(200分)

  • 主题发起人 主题发起人 Sachow
  • 开始时间 开始时间
S

Sachow

Unregistered / Unconfirmed
GUEST, unregistred user!
最近小弟受上司之命,要做一个屏幕保护程序,以作为公司的形象宣传手段之一,现在程序最近小弟受上司之命,要做一个屏幕保护程序,以作为公司的形象宣传手段之一,现在程序
已经完成,但发现一个致命问题:内存泄漏!每当程序运行到超过30分钟以上(按每3秒切
换一次图片来计)时就会耗尽内存而死机。
由于代码很长,不能全部贴出来,我就简单地描述一下程序结构:程序主要有两个部分,
一是主窗体,用来接受用户的输入及画图,二是一个线程对象,用来不停地执行画图函数。
这个线程很简单,只有下列语句:(请别见怪,这是C++ Builder做的,我认为可以与Delphi
代码一视同仁)
void __fastcall SvrThrd::Execute(void)
{
while(!Terminated){
Sleep(SvrForm->IntervalTime); //切换图片的间隔
SvrForm->PlayPic(); //执行主窗体的切换图片函数
}
}
而所有的变量及功能函数都是定义在主窗体SvrForm中的,属于窗体类的成员方法,它们
包括读取文件头的函数,读取文件信息表的函数,读取图片的函数,画图函数及调用画图
函数的辅助函数等,补充一下,我的屏保数据文件是用TFileStream写入的文件,它包括了
一个文件头,三个文件信息表和数据段,读取的时候也是用TFileStream来读出的,由于数
据文件中的图片都是Jpeg图片,因此我的读取图片方式是用是TJPEGImage::LoadFromStream()。
下面的读取图片函数:
//---------------------------------------------------------------------------
bool __fastcall TSvrForm::LoadPic(Graphics::TBitmap *Dest)
{
/* 为了考虑性能,我把下面的变量都设成了公有或私有变量,而不是局部变量 */
try{
MM->Seek(PicInfo[PicIndex].FileBegin, soFromBeginning);
//MM是一个TfileStream*,由FormCreate时初始化
//PicInfo是图片信息表结构指针数组,由读取文件信息表函数读得其值
//PicIndex是一个全局整型变量,用来标识当前读取的图片序号
if (PicInfo[PicIndex].FileType == 1){ //资源为Bmp图片
Dest->LoadFromStream(MM);
}
else if (PicInfo[PicIndex].FileType == 2){ //资源为Jpeg图片
//tmpjpg是一个TJPEGImage*,由FormCreate时初始化
//怀疑以下两句可能有导致内存泄漏的因素
tmpjpg->LoadFromStream(MM);
Dest->Assign(tmpjpg);
}
if (PicIndex<PFH.PictureCount-1)
//PFH是文件头结构,其PictureCount成员标识了数据文件中的图片数量
//PFH结构由读取文件头函数读得其值
PicIndex++;
else
PicIndex = 0;
return true;
}
catch(...){
return false;
}
}
//---------------------------------------------------------------------------
列出此函数是因为该函数是程序中仅有的两个反复地被线程执行的函数之一,另一个是
画图函数,它只是简单地执行画布的矩形拷贝(Canvas->CopyRect()),因些不被考
虑为导致内存泄漏的原因之一,在每次画完图(即完成图片切换效果的绘制)后才执行
读入图片LoadPic函数。

我认为可能导致内存泄漏的地方有以下方面:
1、TJPEGImage::LoadFromStream()有问题;
2、位图的Assign(tmpjpg)有问题;
3、通过一个线程对象去执行另一个类的方法会出问题;
在先前我的LoadPic函数中用的tmpjpg和MM都还是局部变量,因此每次都要用new
来分配内存,用delete来释放内存,这样执行次数多了以后,更增加了内存碎片的产
生,故才改为SvrForm窗体类的私有变量。

望各位大侠帮忙分析一下问题是出在哪里的,另外如果谁能给我一份Bounds Checker
for C++ Builder就更好了,万分感激!此题目(200+)的意思是不够再加!
 
如果能给我一份正式版的Stardust ScreenSaver ToolKit 2.1(一个比较好的做屏幕保护
程序的工具)也有酬谢!
 
http://www.newhua.com/StardustScreen.htm
有Stardust Screen Saver Toolkit 2.1.56及其汉化(破解?)
 
最近小弟受上司之命,要做一个屏幕保护程序,以作为公司的形象宣传手段之一,现在程序
已经完成,但发现一个致命问题:内存泄漏!每当程序运行到超过30分钟以上(按每3秒切
换一次图片来计)时就会耗尽内存而死机。
由于代码很长,不能全部贴出来,我就简单地描述一下程序结构:程序主要有两个部分,
一是主窗体,用来接受用户的输入及画图,二是一个线程对象,用来不停地执行画图函数。
这个线程很简单,只有下列语句:(请别见怪,这是C++ Builder做的,我认为可以与Delphi
代码一视同仁)
void __fastcall SvrThrd::Execute(void)
{
while(!Terminated){
Sleep(SvrForm->IntervalTime); //切换图片的间隔
SvrForm->PlayPic(); //执行主窗体的切换图片函数
}
}
而所有的变量及功能函数都是定义在主窗体SvrForm中的,属于窗体类的成员方法,它们
包括读取文件头的函数,读取文件信息表的函数,读取图片的函数,画图函数及调用画图
函数的辅助函数等,补充一下,我的屏保数据文件是用TFileStream写入的文件,它包括了
一个文件头,三个文件信息表和数据段,读取的时候也是用TFileStream来读出的,由于数
据文件中的图片都是Jpeg图片,因此我的读取图片方式是用是TJPEGImage::LoadFromStream()。
下面的读取图片函数:
//---------------------------------------------------------------------------
bool __fastcall TSvrForm::LoadPic(Graphics::TBitmap *Dest)
{
/* 为了考虑性能,我把下面的变量都设成了公有或私有变量,而不是局部变量 */
try{
MM->Seek(PicInfo[PicIndex].FileBegin, soFromBeginning);
//MM是一个TfileStream*,由FormCreate时初始化
//PicInfo是图片信息表结构指针数组,由读取文件信息表函数读得其值
//PicIndex是一个全局整型变量,用来标识当前读取的图片序号
if (PicInfo[PicIndex].FileType == 1){ //资源为Bmp图片
Dest->LoadFromStream(MM);
}
else if (PicInfo[PicIndex].FileType == 2){ //资源为Jpeg图片
//tmpjpg是一个TJPEGImage*,由FormCreate时初始化
//怀疑以下两句可能有导致内存泄漏的因素
tmpjpg->LoadFromStream(MM);
Dest->Assign(tmpjpg);
}
if (PicIndex<PFH.PictureCount-1)
//PFH是文件头结构,其PictureCount成员标识了数据文件中的图片数量
//PFH结构由读取文件头函数读得其值
PicIndex++;
else
PicIndex = 0;
return true;
}
catch(...){
return false;
}
}
//---------------------------------------------------------------------------
列出此函数是因为该函数是程序中仅有的两个反复地被线程执行的函数之一,另一个是
画图函数,它只是简单地执行画布的矩形拷贝(Canvas->CopyRect()),因些不被考
虑为导致内存泄漏的原因之一,在每次画完图(即完成图片切换效果的绘制)后才执行
读入图片LoadPic函数。

我认为可能导致内存泄漏的地方有以下方面:
1、TJPEGImage::LoadFromStream()有问题;
2、位图的Assign(tmpjpg)有问题;
3、通过一个线程对象去执行另一个类的方法会出问题;
在先前我的LoadPic函数中用的tmpjpg和MM都还是局部变量,因此每次都要用new
来分配内存,用delete来释放内存,这样执行次数多了以后,更增加了内存碎片的产
生,故才改为SvrForm窗体类的私有变量。

望各位大侠帮忙分析一下问题是出在哪里的,另外如果谁能给我一份Bounds Checker
for C++ Builder就更好了,万分感激!此题目(200+)的意思是不够再加!
 
有这摸多屏保程序不用,干吗自己再编一个,想不通!
 
cactus123456: 别人做的屏保制作程序有现成的正式版吗?要花美元买!老板可不答应,
另外,别人做的程序都会有它的版权信息,于我公司宣传形象的初衷不符。
Bakubaku: 别跟我开玩笑嘛!
 
没开玩笑,帮你补全一下,你的贴子中有小于号,被吃掉了一半,难道没看出来吗?:-)

问题应该出在这里,看看 TBitmap.LoadFromStream 的定义:
procedure TBitmap.LoadFromStream(Stream: TStream);
begin
ReadStream(Stream, Stream.Size - Stream.Position);
end;
按照你的说法,你是在一个文件流里面保存了多个图像信息,然后用 PicInfo 和 PicIndex
存取。但是 TBitmap.LoadFromStream 不管你的流里面有多少信息,总是把从当前位置到末
尾的整个流都读进来(Stream.Size - Stream.Position),内存泄漏应该就在这里发生的。

解决的办法是把所需的某个图像信息 Copy 到一个新的 MemoryStream ,然后再让 LoadFromStream
去读取。这个程序我不好测试,所以仅仅是推测而已。
 
我认为你就应该在开始屏保之前将所有的图片都读入内存, 然后一个个地切换关联,
反正图片应该不是很多的, 否则就要考虑你做的这个屏保的构思和价值了.
 
改用Flash做吧, 又简单, 又容易做的炫
 
非常感谢BaKuBaKu!你真是一语惊醒梦中人!我再分析分析。
IKnow: 把所有图片读入内存可是巨耗系统资源呀,一幅800X600的Bmp就有1.4M的体积,如
果10幅的话,在程序计算量大时一次就要占用16M以上的内存(加上程序本身需要的内存以
及分配置几个大数组需要的内存),这可了不得呀。
 
BaKuBaKu: 我已经改了一种方式,即每次用一个内存流(TMemoryStream)从文件流中读出
图片(我的图片用的都是JPEG图片,所以要先读出JPEG图片,再把它Assign到位图对象中),
如下面所列出的:
MM->Seek(PicInfo[PicIndex].FileBegin, soFromBeginning); //定位到资源位置
PS->Clear(); //清除原来内存流中的内容
//用内存流PS读取资源
PS->CopyFrom(MM, PicInfo[PicIndex].FileSize); //从文件流中读出原图片的内容
PS->Seek(0,soFromBeginning);
...
else if (PicInfo[PicIndex].FileType == 2){ //资源为Jpeg图片
tmpjpg->LoadFromStream(PS);
Dest->Assign(tmpjpg);
}
但运行时间一长,仍然死机。你有没有空帮我分析一下源代码,如果有的话我发过去给你?
 
如果真是这样,建议你定点调试,比如你怀疑是 LoadPic 出问题,那么就搞一个循环:
while true do
begin
PS->Seek(0,soFromBeginning);
tmpjpg->LoadFromStream(PS);
end
如果不出问题,那么就
while true do
begin
Dest->Assign(tmpjpg);
end
因为 LoadPic 出问题只可能在这里,这样肯定能定位到出错的位置!不过我怀疑还有别的
地方有问题,因为 LoadFromStream 和 Assign 的代码我看了一下,好像没有别的问题,
Assign 在读入新的数据之前都把原来的释放了。

你把代码发过来也可以,我试试看吧!
 
内存泄漏?这是一个新名词?恕老夫孤陋寡闻了,呵呵。
我想应该是内存申请-分配-释放出了问题,鉴于程序是C++的,
我想指针操作部分出问题的可能也很大,可以采取对可以的代码反复调用测试,
将故障定位。
 
呵呵,内存泄漏这个新名词可不是我发明的,很多C++的教材上都是这样叫的。
 
编程之前先搜搜,以免做无用功
http://vcl.vclxx.org/DELPHIGB/DEFAULT.HTM
用 屏幕保护 搜搜,会对你有用的,如果还帮不上忙,
我才会帮助你的
 
C++是不是没有Delphi安全?
 
cactus123456 :
深度历险上的查过了,也下载了一些,要么编译通不过,要么太简单,不能符合要求,所以
才自己做的。
han: 我也有过这种考虑,但确切地说不是C++没有Delphi安全,而是因为C++ Builder中所
有的VCL类对象都必须定义为指针,出错的机率就更大一些。
 
我有个很菜的sample
Screen Saver Configuration Form
by Corbin Dunn
 
昨天下班时做了一个试险,证实即使LoadFromFile也会死机,现在死机原因的范围要重新
考虑:
1、JPEG单元本身有问题,在读取少量图片进行操作时感觉不到,多了就出问题?
2、在使用Canvas的矩形拷贝时消耗的内存不能释放?
例如:
Canvas->CopyRect(Rect(0,0,Width,Height),Bmp1->Canvas,Rect(0,0,Width,Height))
 
后退
顶部