Am I crazy? Is there a bug in a so simple code?
Hi everybody,
I am trying to find out a memory leak in my program. I found out that I was loosing some memory when I was destroy a child modeless dialog box. So, I created a new project in Visual Studio 2005. I create an MDI Application, I use the default parameters of the project wizard.
Then I use the resource editor to create a simple modeless child dialog window and create a class with the wizard for this dialog (CDialTest). So far, very simple.
Then I added the following code in the "InitInstance" function of my application:
BOOL CTestApp::InitInstance() {
...
...
pMainFrame->UpdateWindow();
while(1) {
CDialTest *up_dial = new CDialTest(AfxGetApp()->m_pMainWnd);
up_dial->Create(IDD_DIALOG1, AfxGetApp()->m_pMainWnd);
up_dial->DestroyWindow();
delete up_dial;
}
return TRUE;
}
When I start this program, the task manager of windows vista tells me that I am slowly loosing memory (30KB per second). I can't understand why? I don't know what I can do about it. Does anyone have an idea?
Thank you in advance.
Re: Am I crazy? Is there a bug in a so simple code?
plz share the rest of the file also.
Enjoy :wave:
Re: Am I crazy? Is there a bug in a so simple code?
Thanks a lot for you reply but the line "delete up_dial;" is suppose to delete my object. Am I wrong? If yes, then how should I delete it?
Re: Am I crazy? Is there a bug in a so simple code?
well first thing I notice is your while loop. That will run for ever. How do you break out of that loop? Also try to remove the call to destroyWindow.
Laitinen
Re: Am I crazy? Is there a bug in a so simple code?
The rest of the file is exactly the same than the one generated by the wizard.
here it is:
DialTest.h:
#pragma once
// CDialTest dialog
class CDialTest : public CDialog
{
DECLARE_DYNAMIC(CDialTest)
public:
CDialTest(CWnd* pParent = NULL); // standard constructor
virtual ~CDialTest();
// Dialog Data
enum { IDD = IDD_DIALOG1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
};
// DialTest.cpp : implementation file
//
#include "stdafx.h"
#include "test.h"
#include "DialTest.h"
// CDialTest dialog
IMPLEMENT_DYNAMIC(CDialTest, CDialog)
CDialTest::CDialTest(CWnd* pParent /*=NULL*/)
: CDialog(CDialTest::IDD, pParent)
{
}
CDialTest::~CDialTest()
{
}
void CDialTest::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDialTest, CDialog)
END_MESSAGE_MAP()
// CDialTest message handlers
just the initinstance function of my app:
// CtestApp initialization
BOOL CtestApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need
// Change the registry key under which our settings are stored
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(4); // Load standard INI file options (including MRU)
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_testTYPE,
RUNTIME_CLASS(CtestDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CtestView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// call DragAcceptFiles only if there's a suffix
// In an MDI app, this should occur immediately after setting m_pMainWnd
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
while (1) {
CDialTest *up_dial = new CDialTest(AfxGetApp()->m_pMainWnd);
up_dial->Create(IDD_DIALOG1, AfxGetApp()->m_pMainWnd);
up_dial->DestroyWindow();
delete up_dial;
}
return TRUE;
}
You have everything. It is very simple code.
Re: Am I crazy? Is there a bug in a so simple code?
Re: Am I crazy? Is there a bug in a so simple code?
The while loop is just to create and destroy forever my dialog window. The only meaning is too see the memory leak through the task manager. I don't need to stop this loop, I simply destroy the processus with the task manager. This loop is for testing purpose.
I tried also without the DestroyWindow but it is the same, I am still loosing the same amout of memory.
Re: Am I crazy? Is there a bug in a so simple code?
Destroys the Windows window attached to the CWnd object. The DestroyWindow member function sends appropriate messages to the window to deactivate it and remove the input focus. It also destroys the window’s menu, flushes the application queue, destroys outstanding timers, removes Clipboard ownership, and breaks the Clipboard-viewer chain if CWnd is at the top of the viewer chain. It sends WM_DESTROY and WM_NCDESTROY messages to the window. It does not destroy the CWnd object.
DestroyWindow is a place holder for performing cleanup. Because DestroyWindow is a virtual function, it is shown in any CWnd-derived class in ClassWizard. But even though you override this function in your CWnd-derived class, DestroyWindow is not necessarily called. If DestroyWindow is not called in the MFC code, then you have to explicitly call it in your own code if you want it to be called.
I think thats what the problem might be,
The CWnd needs to be destroyed explicitly.
:wave:
Re: Am I crazy? Is there a bug in a so simple code?
Quote:
Originally Posted by VictorN
Nice link.
Re: Am I crazy? Is there a bug in a so simple code?
Task manager does NOT show you any memory leak, only the amount of address space that is in use! The quote from the J.Newcomer's essay I referred to in my previous post:
Quote:
But I Free The Space!
A common misconception is that these numbers tell something about the memory your program is consuming. As I point out, this is not what those numbers tell you. They tell you the amount of address space that is in use. This has nothing to do with the amount of that address space your program is actually using.
For example, when you allocate a block of storage, the allocator may go to the kernel to get more address space. It then fills that address space in with some headers and other information. As you allocate the storage, it partitions that address space into smaller blocks. I have never looked into when address space is actually released back to the kernel, but it doesn't happen very often.
In addition, say you allocated 20,000 elements of 100 bytes each. You will see your address space grow by 2,000,000 bytes. Then you release the storage and your address space doesn't shrink. Did you fail to release it? Is there a bug?
No. You probably released it, and it is not a bug that you are seeing. What you now have are 20,000 little free blocks of 100 bytes each on the free list. They are still in the address space, but they are available for subsequent allocation. Actually, many of these will be merged into larger blocks, but the address space remains allocated. Consequently, the numbers you see in task manager are almost but not quite completely useless for telling how much of your memory is in use. If you see it shrink, this is Good, but if you don't, it is not necessarily Bad.
So you really can't trust these numbers. When I care, I use the heap walking functions to track what is going on.
At some point, I'm going to do something about a tool for studying heap fragmentation. MFC provides some nice tools for this. But for now, all I can suggest is exercising due diligence in handling allocation. Use a tool like Bounds Checker for Windows (from NuMega) to verify you are not leaking memory and other resources.
Re: Am I crazy? Is there a bug in a so simple code?
Hi salman, Hi victor,
Thank you for helping me.
In my code in the while loop, I do call explicity DestroyWindow for my dialog and I do destroy explicitely my dialog by the "delete" function. Am I wrong?
Victor, thanks for your article. I thought also that i souldn't trust so much the task manager but it keeps increasing. It started from 500Ko and now in about 10 minutes, it is 5MB. Do you really think I should not care about it? I first though it may increase up to a value but it sounds like there is no limit in the memory consumption.
Re: Am I crazy? Is there a bug in a so simple code?
hi
can u tell me how to post a thread through code guru, i didn't found any option. can u plz tell me
Re: Am I crazy? Is there a bug in a so simple code?
go to the discussion forum on codeguru main page. Then choose your forum (Visual C++ programming for exemple). Then there is a "New thread" button. Good luck.
Re: Am I crazy? Is there a bug in a so simple code?
Quote:
Originally Posted by madric
Hi salman, Hi victor,
Thank you for helping me.
In my code in the while loop, I do call explicity DestroyWindow for my dialog and I do destroy explicitely my dialog by the "delete" function. Am I wrong?
Victor, thanks for your article. I thought also that i souldn't trust so much the task manager but it keeps increasing. It started from 500Ko and now in about 10 minutes, it is 5MB. Do you really think I should not care about it? I first though it may increase up to a value but it sounds like there is no limit in the memory consumption.
There is definitly no leak in the code you have shown here, the probabal reason of memory consumption going up could be the way Win32 DestroyWindow API behaves it's internal memory, it's possible that call to DestroyWindow does not actually mean that all the memory related to that window is cleaned up by windows, it could be coded to release it on certain other event.
The only way to confirm that there is no leak is to call EmptyWorkingSet either after each call to DestroyWindow Or periodically and see if the memory consumption shown in task manager settles down to base level.
If the memory consumption is still increasing then there could be problem, mostly in Win32 API, which you can report to microsoft along with the sample which can reproduce it.
Code:
BOOL CTestApp::InitInstance() {
...
...
pMainFrame->UpdateWindow();
while(1) {
// checkpoint 1...
CDialTest *up_dial = new CDialTest(AfxGetApp()->m_pMainWnd);
up_dial->Create(IDD_DIALOG1, AfxGetApp()->m_pMainWnd);
up_dial->DestroyWindow();
delete up_dial;
EmptyWorkingSet(GetCurrentProcess());
// checkpoint 2... after this the consumption should come (always) around
// the amount recorded at checkpoint 1
}
return TRUE;
}
Re: Am I crazy? Is there a bug in a so simple code?
Hi, I know this is an old topic but I have the same problem.
I do Create and Destroy a dialog as described above. There are no memory leaks reported by the debugger. Checking CMemoryState shows no leak. But still the memory is growing slowly, after roughly 10000 times about 1MB. I've seen it going up to 60MB so far. (Tested with both MFC class and WinAPI only)
What do you think? Heap fragmentation? Any workaround tips?