Link to home
Start Free TrialLog in
Avatar of sllsgl
sllsgl

asked on

GlobalAlloc vs new + PurifyNT

VC++5, NT4 SP1, MsCOMM32.ocx, DAO35
-----------------------------------
Hi, all

If I wanted a circular buffer allocated in one function to be accessable to other functions, can I use "new" to allocate the memory???

The reason I asked this is because I have tried a runtime error checking program PurifyNT on my app & it reported uninitialize memory access on the circular buffer.  I am able to run my app well but after collecting about 2000 records from the COM ports, I suddenly encountered this weird thing : my app closes down without even Dr Watson or any error messages ???  What's happening ???  The only thing I noticed is that the program starts "sucking up" memory tremedously just before it closes ???

My app is a DAO35 MFC AppWizard Generated Program using VC++5 in WinNT4 SP1.  The objective is to capture all inputs from COM ports into an Access97 database.

Noticing this "smooth" termination, I ran PurifyNT & noticed this uninitialized warnings.  In your expert opinion, must I replace those "new" operator to GlobalAlloc() & GlobalLock() if I wanted the circular buffer to be safely allocated & recognized.

Hope anyone can help, it's very urgent :(

Thanks alot for any info :)

PS. PurifyNT also warns many standard DLLs (Kernel32, OleAut32, OleVar, etc) Memory Leak or uninitalize warnings on the standard lib functions (eg, CDaoRecordSet:Open(), GetFieldValue(), SetFieldValue(), etc), Do I have to bother about them ???  I have never altered their codes.  Do I need any patches ??
Avatar of nietod
nietod

You can and probably should use new instead of Globalalloc().  That is not the cause of the problem.
How does you application terminate?  Is the OS shutting it down because of an exception or something?  Or does it terminate normally, that is return from main(), but at a time you don't expect it to.

You say it grabs memory, do you know that the memory has to do with this circular buffer?  I wouldn't think that is the case, because I  would expect that you allocate the buffer at the start of the program and note deal with its memory allocation after that.
About the uniitialized memory access, should the buffet be initialized?  If so, how do you initialize it?  

Also do you know that VC has some good built-in memory error  checking features.  If you leak memory, it will show the exact lines where the memory was created.  If you overrun the edges of a block of memory, it will detect it a report it.  It also picks up some other errors.  You should be using these features.
new usually uses HeapAlloc() (which is better than GlobalAlloc() for most uses).

[Todd, this question's placement seems OK to me.  If there's a bug in EE, I don't see it.]
Now it looks okay to me too.  Maybe the bug is fixed or maybe I should use milk instead of vodka on my cereal.
Avatar of sllsgl

ASKER

what are the VC++ error checking features ?? can u elaborate a little more ?
I have a global link list keeping track of all the allocated circular buffer addresses.  The buffer is allocated using

typedef struct {
  char dblock[65536];
  int front;
  int rear;
} RxType

typedef struct MyListTag {
   RxType *buffer;
   MyListTag *next;
} MyListType;

MyList *active;   // Global
int totalActive;

{ // This is done in one function in the CAppView Class
  //First allocate for active;
  // excluding all error checking
  active = new MyList;
  active->buffer=new RxType;
  active->buffer->front=active->buffer->rear=0;
  active->buffer->dblock[0]='\0';
  active->next=NULL;  // For multiple port opened, this list will link on.
}

{ In other functions in the same class, I still need to access the activeList & the buffer
  ...
  lstrcat(active->buffer->dblock + active->buffer->rear, tmpstr);
  ....
}

{  In a KillPort function again in the same class

delete active->buffer;
active->buffer=NULL;
delete active;
active=NULL;
}

The program can run smoothly to receive on an average of 2000 messages but after that the consumed memory will shoot up even when nothing is received & finally the program closes down without any error messages .

Pls help guide me in tracking the errors ???

Thanks alot :)
Avatar of sllsgl

ASKER

In fact if the circular buffer is properly allocated using "new" there shouldn't be any significant reasons why the memory will keep on increasing when messages are received, right ???  Maybe except for the listbox that is showing all inputs but there is a limit for the listbox items & when the limit is reached, I DeleteString the topmost quarter of the total items.  

I have not allocated anymore memory after those stated in the previous comment, so I am wondering if there is possibilities that Adding New Record onto the Access database using DAO35 is the cause for the increasing memory usage.  My Database is accessed using Dynaset recordset throughout the entire program.

Thanks alot for any info :)

PS. But if new is better than GlobalAlloc() why does PurifyNT identify the buffer as uninitialized memory area ??  I am starting to doubt PurifyNT ... ;(
It seems like you are building a linked list of MyListTags., is that right?

Your code that deletes these tags

delete active->buffer;
active->buffer=NULL;
delete active;
active=NULL;

seems to delete only the first tag in the list, not all the tags.  That would cause a memory leak.  (Unless there is other pertainent coude you haven't posted).
I see the problem.  Answer coming soon.
Things are getting a little jumbled here because I your 2nd comment didn't come through when I posted mine.  Anyway, I don't see the problem.  I read something wrong.

It is a little hard to understand what you are doing when.  The procedures you've showen seem to be correct, but I can't tell that you are using them correctly.  Also I can;t tell if there is anything else that you are doing that might be a problem.  I the code is short enough you could post it here or you could e-mail it to me at nietod@theshop.net, if you want.

I can't tell you anything about the access stuff.   Not my area.  However, if access is running in a seperate process, it probably is not affecting the memroy in your process (unless there is a problem in the "communication procedures" between the two).

New is better than globalalloc because it allocates all your programs little blocks (not so little in you case) out of one big block obrained by the operating sytem.  This means that the operating system only has to track one block (many really, but not as many) for your program, rather than tracking lots of blocks.  This has nothing to do with the initialization issue.  I'm not familiar with the purifyNT utuility, but it is probably detecting a bug where you try to read data from your dynamically allocated memory before you ever write to it.

To get these sort of features enabled in VC, use the _CrtSetDbgFlag().  To make it work right you have to use a pre-processor macro to #define new to use an overloaded new operator that embeds the source code file name and line number into a block when it is allocated.  I believe this is all documented.  I can help you if needed.
Are you sure your buffer is in fact circular?  Otherwise, ~2000 allocations of >64KB will cause some problems.

If it is in fact circular, make sure you're not allocating new buffers on reused items.

Check that you update the links correctly whaen you add and remove buffers.

For debugging you can set up a static variable that is incremented on each "active->buffer=new RxType" and decremented on each "delete active->buffer".  
Avatar of sllsgl

ASKER

Nietod, since you say new isn' the problem, I guess I have to reopen the problem first so as to listen to more  opionion.  Anyway, thanks, u have been very helpful :)

      delete active->buffer;
      active->buffer=NULL;
      delete active;
      active=NULL;

This section of codes only show for one active item in the list.  the other following nodes are taken care of nicely.

I actually don't know if my circular buffer is at fault, however it should be the most suspicous item, coz data received is being added to the buffer's rear.  However, the memory still keep on increasing even when the listbox which is supposed to show the incoming data, has reached its preset limit & ResetContent() executed.

For the wrapping portion of the circular buffer, to ensure it wraps nicely, I make a check if the rear is reaching the CIRCLESIZE constant, if it is too near, I will add one char at each time onto the buffer's rear, else I will simply lstrcat the whole received block to the rear.  I have checked that many times with printout to a log files every time the data is added to the buffer before & after.  The logging shows correctly every time.  In addition, to be able to process 2000 messages of char size about 80 each, the wrapping portion would have already be done.  I have also purposely set the buffer's front & rear ptr to 1000 chars before the CIRCLESIZE const so as to fully test the wrapping, both tests did well except for the sudden termination.

A question to ask is how can I know if the memory is freed upon delete operator, do I see a drop in memory occupation of the program instanteously, say in my case a circular buffer of 64k chars ???

Thanks alot for all your help :)

How are you measuring your program's memory consumtion?  If your are using an OS utility of some sort.  It is not likely to see every change produced by new and delete.  This is because new and delete will allocate and free large blocks from the OS and divide them up into smaller blocks as needed by each individual new and delete.

Your original question was
>> If I wanted a circular buffer allocated in one function to be accessable
>> to other functions, can I use "new"  to allocate the memory

I answered that.  I can assure you that you can.  In fact it is better that you do.
Avatar of sllsgl

ASKER

I merely track the memory consumption using the task manager.  Regarding reopening the question, I must apologize but I really need some help in pinpointing my problem.  I may be inaccurate in stating my problem in the first place, hope u don't mind =( Sorry ...
Avatar of sllsgl

ASKER

Also do you know that VC has some good built-in memory error  checking features.  If you leak memory, it will show the exact lines where the memory was created.  If you overrun the edges of a block of memory, it will detect it a report it.  It also picks up some other errors.  You should be using these features. -- By nietod

Nietod, can u help again by briefing guiding me on how to use these features ??

Are you sure your buffer is in fact circular?  Otherwise, ~2000 allocations of >64KB will cause some problems.  If it is in fact circular, make sure you're not allocating new buffers on reused items. -- By Alexo

Alexo, how do u count that ~2000 allocations of 64KB causes problems & what are they?
I didn't "new" the buffer many times, just at the starting & reuses this throughout the program.  Even when I have deleted the original buffer, I cannot reuse this area ???  But I don't control which memory area I am assigned, right ???

Thanks in advance for further help :)
PS. Today, I retested the program & found that the sudden termination problem seems random.  Sometimes, I can receive more than 9300 messages but sometime less than 1000.  Average is around 3000 msg.
>> Alexo, how do u count that ~2000 allocations of 64KB causes problems & what are they?

Simple, 2000 * (64KB + overhead) > 128MB.  You may be running out of swap.

>> I didn't "new" the buffer many times, just at the starting & reuses this throughout the program.  Even when I have deleted the original buffer, I cannot reuse this area ???

Just make sure you delete the previous buffer.
There isn't enough information here for us to help you.  (Without a really lucky guess, which doesn't happen often enough.)  Can you post more of your code?  Or if it is long, you can e-mail it to us.  My address is nietod@theshop.net.  Alexo's address is on his expert info page.  (Although he didn't envite you to send the code here, he has made the offer on other occasions, so I don't think he would mind.)
To use VC's memory debugging features you use the procedure _CrtSetDbgFlg().  with the _CRTDBG_LEAK_CHECK_DF flag.  (Or this flag in with the existing flags.  This will report memory block overruns and blocks that weren't deleted when the program terminates. To make it really effective, though you need to overload the new operator so that it stores with each block the source code file name and line number from which thr block was allocated.  Then when you gets these errors reported, you can doubleclick the error message and it will
show you the line of code that allocated the block.  This is all documented in the VC help.
Avatar of sllsgl

ASKER

Err, this VC memory check sounds pretty confusing, especially I don't feel comfortable with overloading, let alone overloading "new" :(

I will send u the file tomorrow.  It consists of the App view class which happens to control most of my program, hope u can see something from it.  But I must apologize first, because I think my code is not very orgranized, & this class is really big.  I will try to put in more explanation remarks to help u understand it better :)

Anyway, thank you very much :) Both Alexo & Nietod have been very helpful :)

Its not that confussing and it is invaluable.  Once you've got it working (and it isn't that hard to get working) you'll wonder how you got by without it.  I've been using it for about a year and it must of caught a dozen mistakes now that would never have been detected otherwise.
Avatar of sllsgl

ASKER

Hi, I cannot send the file yet, coz I forgot to bring home the file ;)  Now u know I am such a pathetic industrial attachment student at this company which doesn't provide WWW access to us lowly species ... sign ..

Anyway, I realized that the increasing memory consumption is due to the increasing Dynaset Recordset I am opening in the background.  So as more & more records have been added, the memory consumption simply increases.  As for the wrapping section, I am now sure that they wrap nicely round the corners as I have managed to grab more than 10000 messages today without crashing.  I am a bit worried the sudden termination may occur any time, especially the time when I present my work :(

So is there a possibility with the CDaoRecordset obj that is causing the sudden termination, any ideas ???

Thanks alot & best regards to all of u :)
Yopu never explained what happens when it terminates.  Does the OS produce an error message (dialog), does the application just quite?  Does it return from main for some unknow reason?
Avatar of sllsgl

ASKER

The strangest point is that the app ended "peacefully" but I didn't make it do so, it closes itself.  The best thing is there isn't any error messages following ;(  

I will mail u my CMsgView Class today.

I have this another question regarding this problematic program.  I did it using MFC42 in VC++5 on WinNT4 SP1.  Now I am going to run this program on some other PCs with no VC++5 installed before.  These program doesn't run as expected.  It gave me errors while I am creating MSCOMM ActiveX control, so I use "regsvr32 mscomm32.ocx" & the message was successful.  However, I still cannot create the control.

I used thui's DllLook & compared every libraries on the two pcs.  I found out that the new PC has a msvcirt.dll which is version 4 while the old one (with VC++5 installed) has a version 5 msvcirt.dll.  This is the only difference I noticed.  Is this the reason why I am getting this error?  How do I upgrade this file on the new PC ???  I have tried overwritting the file & re-register is using "regsvr32 msvcirt.dll" but it gave me error saying no entry point & file may be corrupted, use Pview to kill ..."

Nietod, I really owe you one, I will properly grade u this time :)
I'm afraid there's not going to be a this time.  I don't think I can help you on the new problems.  I'm not sure what msvcirt is.  msvcrt (no "i")is the run-tim library.  But I don't know what the "i" is.  Note however, that VC changed the name mangeling scheme from version 4 to version 5.  This means that the names of the exported functions that appear in DLLs have changed.  Thus an EXE that uses a version 4 DLL will not be able to use a version 5 DLL (unless exports were  extern "C").  For this reason, you should probably be copying the DLLs to the directory containing the EXE, not to a system dirrectory.  That is, if you are using version 4 and version 5 EXEs then you will need seperate copies of the DLLs they use, so they must go in seperate directories.
Avatar of sllsgl

ASKER

Hi, nietod & alexo, & others, do u know how to register an ActiveX Control such as mscomm32.ocx ??

I tried "regsvr32 mscomm32.ocx" but though the message box said "DllRegister Succeed" , my app cannot create an object out of it.  Using OleViewer or Container Test, show that the control is not properly registered, I guessed ?  The only way I did is to install the VC++5 activeX controls from the disc.  This is quite inconvenient & inappropriate as the whole installation is > 8Mb & I need only mscomm ???  Also, not very feasible for a new PC to go through all these to run the app, right ??

Any idea ???

Thanks in advance :)

BTW, you mention using a constructor in my active list and portlist structure ??  May I know briefy how should I do that ???
you had

   portlist=new portdataType;
   portlist->totalport=0;
   portlist->head=NULL;

make the constructor do the last two lines  Change the structure in the .h file, like

struct portdataType
 {
   portdataRec *head;
   int totalport;
   portdataType () :  head(NULL), totalport(0) { };
};

same sort of thing for activelist.   Then you can just create them like

  portlist=new portdataType;

You also may be able to benefit by writing destructors.  I didn't look into that.
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of sllsgl

ASKER

Nietod, which part are u assuming ? The PurifyNT, registering OCX or destructor ???
Anyway, I came across regocx32.exe inside VB5 cdrom, I am wondering if this works for a newly setup WinNT4 pc as it works perfectly for my home Win95 PC ???

Thanks for any info contributed :)
Sorry, I had answered the original problem and some of the additional ones and there didn't seem to be any activity so I thought the problem was gone....

I had suggested some changes to the code and never heard from you again.  I assume they didn't help?  Did you want to send the cleaned up code?
Avatar of sllsgl

ASKER

Okay nietod, this is for u :)
My app is undergoing testing now, at the moment this built is still going strong, hopefully the sudden termination will not happen again :)
My last testing shows that it can tolerate > 10,000 records without sign of crashing, do u think it's safe to conclude that ?

There is only one last concern, and that is the DaoRecordset doesn't seems to return its occupied memory even after I have executed a SQL statement on its database object to remove all its records, do u happen to know the reason to this ???

Thanks alot for your constant help :)
I would say it is a good sign.  Just test it alot and see.

I'm not familaire with the DaoRecordSet (in any way).  However, many objects won't return their memory until they are destroyed.  for example you miight have an array class that allows the array to grow and shrink.  As it gros it might consume more memory, but as it shrinks it might retain the memory and reserve it for future expansion rather than return it to the heap.  The array should return the memory when it is destroyed, however.  The recordset might be using this type of design--but i don;t know.  I'm not even sure of the nature, of the DaoRecordSet.  Is it a refgualr C++ object, if so you should be able to reclaim the memory by destroying it.  If no, can you have whatever produced it destrory it for you?  (For example this is the way the windows OS works.  When you need a bitmap you call CreateBitmap to create it and it uses memory.  When you are done you call DeleteObject() and windows deletes the bitmap and returns its memory to the heap.)

I hope this helps.  

I did not mean to "extort" points from you.  I really thought you were done.
Avatar of sllsgl

ASKER

Nietod,  trust me I am very grateful to your continous help :)
CDaoRecordset is a standard C++ object, but my program needs the object to exist throughout the application, hence there is no way I can destroy it :(  

I thought a Requery() after the removal process may help, but it doesn't :(  See a recordset is merely a mirror of what u extracted from the database object, so I am actually reflecting my whole database records in this set, hence the memory should grow when more records are added.  I'm just curious why it doesn't return those memory when the old records have been removed from the database ???

Anyway, thanks alot, nietod, u jolly deserve my points =)

Thanks in deep appreciation :)
Even though you need A recordset throught out the program, it doesn't mean you need the same one throught out the entire program.  If there are times when the object contains no records, you might be able to get back memory by allocating the object with new (rather than using what I assume isa global variable).   When the program starts you allocate the object with new and save the pointer. When the object is empty, say after you've cleared all the records out of it (I'm guessing at what is going on) you delete the object and immediately allocate a new one save its pointer again.

Depending on your circumstances, that might work.
Avatar of sllsgl

ASKER

That's a bit complex, I'm afraid :(  This object is not created using "new" & freed using "delete".  It's created using CDaoRecordset::Open(....), & no destroy except Close().  So I don't think I can delete the object except closing my app :)

Thanks Nietod :)
Is Open() a static function that returns a new CDaoRecordSet object?  If so, then use open and close in the above example instead of new and delete.  Use close to destroy the object and then use Open to immediately create a new one.  i.e.

CDaoRecordSet *RecordSet = CDaoRecordSet::Open();

     *  *  *
RecordSet->Close();
RecordSet = CDaoRecordSet::Open();


Avatar of sllsgl

ASKER

I have performed this before, only see the memory consumption going up every time instead of reducing.  

In the case when I need to Compact a Database, I have to close all related obj of recordset (such as CDaoRecordset, CDaoDatabase, & CDaoWorkspace) before performing the Compact, there after, I will reopen all objects again, the memory never reduces :( in this instance too.

Thanks Nietod :)