Memory usage in GUI for .NET

Posted by alextrs on 20-Jan-2010 07:33

I'm trying to delevelop system from scratch on .NET, I already have a couple screens and at one moment I decided to look and check how much memory my application "consumes". And I was really amazed by what I saw. Ok, by default my MDI screen takes 35 Mb, but when I open additional Form is takes 0.5 - 2 Mb more and this is too ok, but why those 0.5 - 2 Mb do not release when I close this Form (releases maybe 30%) ? I try to use MemoryLeaking tools, and all objects created by form delete, I look into log file and see that objects are deleteted and GK works too. So where my memory goes? Maybe I can trace somehow what's wrong or change some keys or do something to fix this, because for client-server application it's ok (from 35 until 256/512 is a long way ), for terminal server environments it means fast death.

Any help appreciated.

All Replies

Posted by Admin on 20-Jan-2010 07:46

Maybe your forms start using a new .NET assembly the first time (like UltraGrid)? Then those forms have caused that assembly (.dll file) to be loaded.

The AVM also is good at grabbing a little more memory over time and not releasing it when a program is closed:

- client record cache

- R-Code segments kept in memory

- etc.

So in the end everything might be just fine when you are sure that all objects and widget handles are released.

Posted by alextrs on 20-Jan-2010 08:15

Maybe your forms start using a new .NET assembly the first time (like UltraGrid)? Then those forms have caused that assembly (.dll file) to be loaded.

When I try to re-open by loop one and the same Form,the memory usage grows too (sure,a little bit less than the first execution, but still it's not acceptable).

The AVM also is good at grabbing a little more memory over time and not releasing it when a program is closed:

- client record cache

- R-Code segments kept in memory

- etc.

Yep, I agree with that, but 1-2 Mb per form is too much

So in the end everything might be just fine when you are sure that all objects and widget handles are released.

I took termserver with 2 Gb RAMM, run my main screen - 20 times (like for 20 users), and then tried to open 100 times one/the same Form and it took the whole memory from server .

Something is really wrong with that

Posted by Thomas Mercer-Hursh on 20-Jan-2010 11:16

It does seem to suggest that something you think is being released, isn't.

Posted by alextrs on 20-Jan-2010 12:26

It does seem to suggest that something you think is being released, isn't.

Exactly, but what? I tried to use http://www.oehive.org/project/Memoryleakfinder because LogRead Tool can't see what is deleted by

Garbage Collector, I also checked log file and it looks for me like everything was deleted:

-logginglevel 3
-logentrytypes 4GLTrace,DynObjects.*,QryInfo
-clientlog c:\temp\OutLog.txt

Posted by Thomas Mercer-Hursh on 20-Jan-2010 12:40

I don't know about the .NET stuff, but have you tried sticking in code to loop through objects to see what is there at various points?

Have you used COMPILE LIST to check the transaction scopes?

Posted by Admin on 21-Jan-2010 00:52

.NET Objects (known to the AVM) will also be placed in the SESSION:FIRST-OBJECT chain. So you might try to loop thru that.

oObject = SESSION:FIRST-OBJECT .

DO WHILE VALID-OBJECT (oObject):

    IF TYPE-OF (oObject, System.Object) THEN

        MESSAGE CAST(oObject):GetType():FullName .

    ELSE

        MESSAGE oObject:GetClass():TypeName .

    oObject = oObject:NEXT-SIBLING .

END.

Similar to that - but of course when you are expecting many objects, the message is not a good output.

I've attached a little form that I wrote recently. I visualizes all available .NET objects (except itself) in a DataGridView and shows (accessible) properties in a PropertyGrid.

Files go into a folder Consultingwerk\Framework\ObjectExplorer

DEFINE VARIABLE oForm AS Progress.Windows.Form NO-UNDO .

oForm = NEW Consultingwerk.Framework.ObjectExplorer.ObjectExplorerForm ().

oForm:Show () .

or when you want to visualize .NET objects after the main WAIT-FOR:

WAIT-FOR System.Windows.Forms.Application:Run (oForm) . 

Please send me back a copy of the file if you make enhancements :-)

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/7485.ObjectExplorerForm.cls.zip:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/8304.ObjectExplorerForm.resx.zip:550:0]

Posted by alextrs on 21-Jan-2010 06:53

Thank you Mike for your form, I'll try to use it today and let you know about the results!

Posted by Peter Judge on 21-Jan-2010 08:06

This looks cool!

But for a lazy old man, can you attach the assemblies file?

-- peter

Posted by Admin on 21-Jan-2010 08:21

It may need

I'm lazy as well and my assemblies.xml file contains tons of other stuff....

Posted by egarcia on 21-Jan-2010 08:39

Hello,

Some few suggestions.

- Comment out portions of the code and see if the extra memory usage goes away.

- Consider the various counters that Windows have for the memory Mem Usage, VM Size in Task Manager (Private Bytes, Virtual Size, Working Set in Process Explorer).

- A .NET profiler could be used to determine whether the memory usage is related to .NET. (Then it could be either internal usage of .NET or ABL usage of .NET, or ABL objects or internal to the OpenEdge client.)

- Remove unnecessary assemblies from the assemblies.xml file. This would reduce the memory usage.

I took termserver with 2 Gb RAMM, run my main screen - 20 times (like for 20 users), and then tried to open 100 times one/the same Form and it took the whole memory from server .

Something is really wrong with that

In regards to the memory usage with terminal server, how much memory is consumed by the form each time?

Perhaps a way to check the memory is to query GC.GetTotalMemory().

See article .NET: how to track memory usage in C# code?

http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/8bfa495b-bed2-40fd-adbd-cb998b74af32

I hope this helps.

Posted by Admin on 21-Jan-2010 10:30

Mike,

Very nice tool. This will be very useful. Got a couple of questions though.

Is there any way to know, from the information available, if an object is associated with a specific form? I know I could cast some of the objects to their specific type and walk up the parent tree, but there are other types where this is not possible.

Also, I noticed there is no Name property in the property grid for controls. Is it possible to get that info into the PropertyGrid? Again, for select object types, I can cast to get the name but I haven't been able to figure out how to get it in the PropertyGrid.

Thanks,

JL

Posted by Admin on 21-Jan-2010 12:51

Is there any way to know, from the information available,

if an object is associated with a specific form?

I doubt there is going to be a generic way.

I know I could cast some of the objects to their specific type

and walk up the parent tree, but there are other types where this

is not possible.

I'd rather be a generic as possible here. CASTing to System.Windows.Forms.Control would be enough for all types of Controls. And for the rest it's getting very specialized. I.e. for UltraToolbars's tools you'll very likely be able to walk up to the owning UltraToolbarsManager and ask that for the Container it's docked in, for other's you'll need similar special ways.

 

Also, I noticed there is no Name property in the property grid for controls.

Is it possible to get that info into the PropertyGrid? Again, for select object

types, I can cast to get the name but I haven't been able to figure out how

to get it in the PropertyGrid.

 

AFAIK you can't influence what's shown in the PropertyGrid. That's defined by the Controls's/Objects' vendor.

If you want the Name property, I'd suggest getting that using Reflection and dynamic calls to the properties's getter. In that case you won't be limited to a common base type, you can make that happen for any Type that has a Name property. I that's not available the ToString() method might be a good substitute.

Posted by Admin on 21-Jan-2010 13:03

If you want the Name property, I'd suggest getting that using Reflection and dynamic calls to the properties's getter. In that case you won't be limited to a common base type, you can make that happen for any Type that has a Name property. I that's not available the ToString() method might be a good substitute.

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/ObjectExplorer_5F00_v0_2C00_2.zip:550:0]

You'll find that enhancement in the attached ZIP.

Posted by Thomas Mercer-Hursh on 21-Jan-2010 13:15

Similar to that - but of course when you are expecting many objects, the message is not a good output.

Yes, but what I was thinking of was the idea of referencing code like this at a point when one thought things should be cleaned up and ready for another iteration.  If it shows that one isn't cleaned up, then it would tell you that the problem was not having cleaned up what one thought one had and what it was.  For that purpose, it can just output to a log file ... doesn't need beauty.

Posted by Admin on 21-Jan-2010 14:02

Cool.

I'll give it a look.

Thanks!

Posted by alextrs on 22-Jan-2010 13:04

Mike, thank you for yours tool, it's really nice!

So I get some results (tried on two PC's):

Windows 7

           Private Bytes  Virtual Size  Working Set
Defualt:    43.844          279.280         55.244
1 Run      45.108          284.948         57.444
Closed     45.052          293.760         57,520
2 Run      46.080          283.916         60.268
Close      46.060          283.896          60.276
10 Run    47.396          284.860          61.792


Objects Qty - 51 (default), after 10 runs - same.
GC:GetTotalMemory(FALSE) - 1.9 Mb (default) and flow 1.9 - 4.0

Windows 2003 Enterprise

            Private Bytes  Virtual Size  Working Set
Defualt:    44.080           232.380        54.336
1 Run       45.344          232.520        55.465
Closed     45.332           232.508        55,448
2 Run       45.964          232.536        56.000
Close       46.060          233.896        60.276
10 Run     51.920          236.804        60.832

Objects Qty - 51 (default), after 10 run - same.
GC:GetTotalMemory(FALSE) - 1.9 Mb (default) and flow 1.9 - 4.0

This thread is closed