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.
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.
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
It does seem to suggest that something you think is being released, isn't.
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
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?
.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]
Thank you Mike for your form, I'll try to use it today and let you know about the results!
This looks cool!
But for a lazy old man, can you attach the assemblies file?
-- peter
It may need
I'm lazy as well and my assemblies.xml file contains tons of other stuff....
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.
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
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.
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.
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.
Cool.
I'll give it a look.
Thanks!
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