How do you figure out how much memory a class is using?

  • Thread starter Thread starter Joe Fallon
  • Start date Start date
J

Joe Fallon

I would like to know how you can figure out how much memory a given instance
of a class is using.

For example, if I load a collection class with 10 items it might use 1KB,
and if I load it with 1000 items it might use 100KB.
How do I measure the amount of memory used once the class is loaded?

Thanks!
 
* "Joe Fallon said:
I would like to know how you can figure out how much memory a given instance
of a class is using.

For example, if I load a collection class with 10 items it might use 1KB,
and if I load it with 1000 items it might use 100KB.
How do I measure the amount of memory used once the class is loaded?

All you can do is using a memory profiler. I am not sure if this will
work as you expect it to do.

<URL:http://www.gotdotnet.com/Community/...mpleGuid=3254325d-a4aa-4bb3-aa86-c72d5104ec74>
 
Herfried,
Thanks for the response.

I am trying code like this:
(Appreciate any comments.)

Dim byteCount As Long = System.GC.GetTotalMemory(False)
mCollection = Collection.GetCollection()

byteCount = System.GC.GetTotalMemory(False) - byteCount

Dim Collectionkb As Double = byteCount / 1024

I ran this code a few times in a row and got varying answers.
65KB, 59KB, 43KB, 47KB, 47KB, 48KB

When I serialize the class to a file on disk it is 30KB.
I thought the rule of thumb was that File size x 2 = Size in Memory.

Can you think of a more reliable way to determine the amount of RAM used by
a class at runtime?
I am trying to decide if I should keep the collection in RAM or serialize it
to disk.
I would like to decide this at run time based on it exceeding a certain
amount of RAM.
 
Joe,
When I serialize the class to a file on disk it is 30KB.
I thought the rule of thumb was that File size x 2 = Size in Memory.
Look at what is actually serialized, there is extra overhead in binary
serialization, as it stores Type & property names in the stream.
I am trying to decide if I should keep the collection in RAM or serialize it
to disk.
I would like to decide this at run time based on it exceeding a certain
amount of RAM.
Are the individual elements a fixed size? I would have a config threshold
option for number of elements, if the collection hits the number of elements
I would go to disk, otherwise I would keep it in memory...

Hope this helps
Jay

Joe Fallon said:
Herfried,
Thanks for the response.

I am trying code like this:
(Appreciate any comments.)

Dim byteCount As Long = System.GC.GetTotalMemory(False)
mCollection = Collection.GetCollection()

byteCount = System.GC.GetTotalMemory(False) - byteCount

Dim Collectionkb As Double = byteCount / 1024

I ran this code a few times in a row and got varying answers.
65KB, 59KB, 43KB, 47KB, 47KB, 48KB

When I serialize the class to a file on disk it is 30KB.
I thought the rule of thumb was that File size x 2 = Size in Memory.

Can you think of a more reliable way to determine the amount of RAM used by
a class at runtime?
I am trying to decide if I should keep the collection in RAM or serialize it
to disk.
I would like to decide this at run time based on it exceeding a certain
amount of RAM.
--
Joe Fallon



Herfried K. Wagner said:
All you can do is using a memory profiler. I am not sure if this will
work as you expect it to do.
<URL:http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=
 
Joe,
There is no way to get the size of a managed object per se.

You could use System.Runtime.InteropServices.Marshal.SizeOf, however this
returns the size of the object when it is marshaled to the unmanaged world,
it does not report the true size of the object in the managed realm.

Also I do not believe the Marshal.SizeOf method takes into account
collection classes, you could use the enumerator of the class to get the
size of each element, unfortunately this will not give you a good idea of
any overhead in the management of the collection...

Hope this helps
Jay
 
Jay,
It helps a lot.

I tried System.Runtime.InteropServices.Marshal.SizeOf on one of my classes
and it failed.

Now that I know it is basically "impossible" I can stop going down that
path.

I decided to use a web.config appsetting flag for this.
When the flag is False then I use Session to store the collection.
When it is True, I will serialize the collection to disk instead.

I only plan to do this for potentially large objects.
Smaller ones will be tossed into Session or Viewstate.

So instead of the normal 2 lines of code (1 to put the object into Session
and 1 to take it out)
I just wrap them in an If statement and check the flag.

So by default, we will store things in Session, but in a pinch can move
large objects to disk and back.
I guess that is a workable approach.

(My original goal was to dynamically test the size of the object and if it
crossed a threshold to move it to disk.)

Thanks for the help Jay!

--
Joe Fallon
Access MVP
 
Joe,
So instead of the normal 2 lines of code (1 to put the object into Session
and 1 to take it out)
I just wrap them in an If statement and check the flag.
I hope you encapsulated your If in a property in a well known place.

In fact I normally encapsulate all access to Session, Viewstate, Cache,
Application, & etc variables in properties in a well known place. This
allows a "single" point of change if I decide that Cache is better then
Session for the variable...

This well known place could either be a shared property of a domain class
(causes coupling to ASP.NET) or a base Page or Control class in ASP.NET. I
normally go for the base Page or Control class, although the domain class
makes its easier (the property is in a single place, verses two places).
Note there are ways to mitigate the coupling to ASP.NET if you choose to put
the property in the domain class...

Hope this helps
Jay
 
Jay,
That is very interesting.
Session, Viewstate, Cache, Application,

Can you elaborate on how you control these with some sample code?

I am sure it is not an "all or nothing" solution. You must be able to
control a given variable (like my large collection) using any one of
Session, Viewstate, Cache, Application. I would like to see how you do it so
I don't go down the wrong path again!

Thanks for another good idea!

--
Joe Fallon



Jay B. Harlow said:
Joe,
So instead of the normal 2 lines of code (1 to put the object into Session
and 1 to take it out)
I just wrap them in an If statement and check the flag.
I hope you encapsulated your If in a property in a well known place.

In fact I normally encapsulate all access to Session, Viewstate, Cache,
Application, & etc variables in properties in a well known place. This
allows a "single" point of change if I decide that Cache is better then
Session for the variable...

This well known place could either be a shared property of a domain class
(causes coupling to ASP.NET) or a base Page or Control class in ASP.NET. I
normally go for the base Page or Control class, although the domain class
makes its easier (the property is in a single place, verses two places).
Note there are ways to mitigate the coupling to ASP.NET if you choose to put
the property in the domain class...

Hope this helps
Jay
 
Joe,
Basically its an extension of the Encapsulate Downcast refactoring.
http://www.refactoring.com/catalog/encapsulateDowncast.html

Rather then have the DirectCast of Session variables scattered throughout my
code, I encapsulate the downcast into a property, normally in a base class.

Normally something like:

' in PageBase.vb file
Public Class PageBase
Inherits System.Web.UI.Page

Public Property ShoppingCart() As ShoppingCartDataSet
Get
Return DirectCast(Session("ShoppingCart"),
ShoppingCartDataSet)
End Get
Set(ByVal value As ShoppingCartDataSet)
Session("ShoppingCart") = value
End Set
End Property

End Class


' in WebForm1.aspx.vb
Public Class WebForm1
Inherits PageBase

Private Sub Page_Load(...) Handles MyBase.Load
DataGrid1.DataSource = Mybase.ShoppingCart.Items
DataGrid1.DataBind()
End Sub

End Class

' in WebForm1.aspx
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb"
Inherits="SampleWebApp.WebForm1" %>


Where ShoppingCartDataSet is a typed dataset that represents the shopping
cart.

Benefits of the above include, but are not limited to:
- Session Variable name is in a single location
- Easy to change from Session to Cache or Disk & back
- Downcast is in a single location
- Enables dynamic creation, the Get could create & initialize the Session
variable if it did not already exist
- The code that consumes the Session variable is cleaner (there is no
downcast & checks)

If your case, the check of the web.config setting would be in a single spot,
the property's Get & Set methods.

Even if the Session variable is used in a single location I would probably
apply the same pattern, as invariable the session variable winds up being
used in multiple places, its more consistent with other code, and it
promotes "cleaner" code.

Hope this helps
Jay

Joe Fallon said:
Jay,
That is very interesting.
Session, Viewstate, Cache, Application,

Can you elaborate on how you control these with some sample code?

I am sure it is not an "all or nothing" solution. You must be able to
control a given variable (like my large collection) using any one of
Session, Viewstate, Cache, Application. I would like to see how you do it so
I don't go down the wrong path again!

Thanks for another good idea!
<<snip>>
 
Doh!
I should add: All web forms within my web app would inherit from the
PageBase class, as I showed for WebForm1.

Hope this helps
Jay
 
Hi Joe,

Are you sure you are not going in that dead ending road, wherefore load
balancing is invented?

Just my thought?

Cor
 
Hi Cor,
No I am not sure! <g>

I agree that load balancing is very valuable.
It is part of the overall design of the app.
All of our Business Objects are Serializable and we can use a StateServer
for a Web Farm.

I am just looking for a way to trade off RAM for "time to serialize to/from
disk" to minimize the impact of large objects on the Session state server
memory.
 
Jay,
I have a Page base class for my app so what you are saying makes sense.

Since I could have dozens (or hundreds) of Business Objects, do you think
the idea of using a Property for each one of them makes sense in the Base
class or should I have a Property on the given page that consumes the BO?

Also, I noted that in the sample Property there was just the use of Session.
Are you saying that you would just edit the Property and change it to
Viewstate if you wanted to change how it is stored?
But wouldn't that require you to re-compile and re-distribute the
application .dll?

I am wondering if it makes sense to be more "configurable" and set flags in
web.config and then use SelectCase in the Property Get/Set to determine the
value of the flag and implement any of these:
Application, Cache, Session, Viewstate.

Then a client who is low on RAM could switch the flag in web.config from
UseSession to UseFiles (or somethinng like that - maybe an Enum) and the app
would behave differently without a re-compilation.

What do you think?
 
Hi Joe,

It was about that saving to disk. I think also that that will be possible,
however that will extend the waiting time in my opinion and than there are
more clients waiting and you again need more memory. Where is than the end
when there are comming more and more to your site.

That was the only reason from my message.

Just a thought about your problem.

Cor
 
Joe,
It really depends on how your Domain objects are organized.

If a Domain object is strictly used by a single Page, I would put the
property in that page's code behind, if it is used by a group of pages, then
I would add a second layer base class for that group of pages. Note: I would
consider 2 pages a group! ;-) For example PageBase is the base page for all
web forms, while TeamPageBase is the base page for "team" web forms, and
UserPageBase is the base page for "user" web forms. Which means both
TeamPageBase & UserPageBase inherit from PageBase.

Alternatively if there is a root Domain object that all other Domain
objects, my PageBase would offer this root...

As far as configurable, generally I "configure" it when I design the class.
This Domain object will always be X, hence my sample is Session, changing it
to Y, would be a design change, based on performance data collected from
running the app. Design changes would require a recompile of the VS.NET
project & redistribute.

If there was a design requirement for it to be "user admin" configurable,
then I would look at creating a provider of sorts, where the web.config had
an entry for what provider to use, and there would be providers for Session,
Viewstate, Cache, Application, and etc...

The provider would follow the Plug-in pattern, similiar to the provider
pattern in ASP.NET 2.0

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspnet/html/asp02182004.asp

Note if I supported the provider option, the settings in the web.config
would be in a custom configuration section.

I would not use a select case in this case, as Select cases are not OO...
However that is a different discussion. See
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
for a start on that discussion ;-)

Hope this helps
Jay
 
Jay,
Thanks a lot for the pointers.

I added some Properties to my Base page and it works perfectly.
After some playing around I decided to offer the 2 choices (Session of file
serialization) and configured the Property Gets and Sets to check the config
file to see which way to handle it.

I appreciate the help!
--
Joe Fallon
 
Back
Top