Misbehaving COM object?

  • Thread starter Thread starter Borco
  • Start date Start date
B

Borco

We have a .NET project that makes pretty regular use of a COM object
that appears to be misbehaving. Am I correct in understanding that
setting a COM object to nothing should act the same way that it would
have in VB6? By this I mean it is actually destroyed and not marked for
GC. We have an object that is called in a polling routine and looks to
be leaking 6-8K at a time and never being reclaimed even though it is
set to nothing. Is this possible in .NET or do we just have a poorly
written COM object?

TIA
 
I personally faced some problems - similar to the one you describe - with
com object myself some times ago.
myObject = nothing
- only releases the .Net pointer reference to this object - and does not
destroy it...
If you have a "poorly written" (memory leaking) com object - this does not
solve your problem.
After checking for a "dispose" method there have been these approaches in my
case:
- System.Runtime.InteropService­s.ReleaseComObject
and the other (yes I know - you should not do that way...)
- GC.Collect.....

Regards
 
Thanks. Unfortunately we are using both of the last two solutions. This
is used in a server app where the load can be pretty high though and it
seems that the GC.Collect calls still do not destroy the object
immediately. I wonder if .NET is really a good choice for applications
like this.
 
Note that VB6 COM objects should run in a STA apartment else you may pay a
serious overhead when marshaling call between incompatible apartments,
that's why it's important to answer following questions:
- What kind of server application is this, a service or a console like
application, what is it written in C# or VB.NET or...-

Willy.
 
But this does not help you when you have a com-object with memory leaks -
does it?
Neither the framework language should make a difference....
 
This is a VB.NET app which is currently running in a console but will
be converted to a service for production. I will look into the
apartment issue. Unfortunately this app (not my design) gets its
threads from a timer and that makes thread control a little less clear.


Thanks for your help.
 
Thanks, I am checking this out right now. Our primary problem is
certainly this old COM object but my understanding (limited and
possibly flawed) of the garbage collection mechanics makes me question
how appropriate it is for a service.

Thanks again...
 
Ok but your COM object is VB6 authored, so at best it's a STA type of
object, that means it should run in a STA initialized thread. Services run
their threads in the MTA as do threadpool threads used by the timers (even
in a console application), so you are in for a couple of surprises.

Willy.
 
I tried to set the CurrentThread.partmentState to STA inside the timer
elapsed event but I could not see any difference in behavior or memory
usage. If I read correctly this will not work if it is already set to
MTA and your post implies that this is the case since we are relying on
a timer for our thread generation. Right now we are lobbying to have
the COM object rewritten in managed code but for various reasons that
might not happen.

To get this thread under proper control it sounds like it would be best
to manually spawn our own threads and control the apartment state
explicitly at creation. Would you agree? I would like to be able to
present alternatives for fixing this code base but am unsure which
direction to take as this particular problem is new for me.
 
Unfortunately, you can't change a thread's apartment state once it has been
set that means you can't change the apartment state of the pool threads,
they are initialized to enter the MTA by the threadpool manager that creates
the threads.
Now what I would suggest is to write a simple console application in which
on the main thread (note that the VB.NET compiler initializes this thread
for STA) you:
- create an instance of the object and,
- call some method.
While running the creation and method call in a loop, you measure the memory
consumption (both native and CLR) using perfmon.exe and measure the call
time.
When done, you do the same on an auxiliary thread initialized for MTA, make
sure the main thread blocks (call Console.ReadLine() after the aux. thread
has started).
Don't call GC.Collect or GC.WaitForPendingFinalizers, watch the Private
Bytes counter for the process, this counter should not change that much
during execution of the loop. If "Private bytes" increases during the STA
test, you have a problem with the COM object, but I guess it won't.
When "Private bytes" grows when running the MTA test, you have a problem
with finalization of the COM reference and you should replace the
Console.ReadLine() by a call to Thread.Join().
Try this and let us know the results.


Willy.
 
OK, I instantiated and ran the COM object on both threads and the
results were nearly identical. The PrivateBytes grows on each call and
never appears to be reclaimed by GC. The amount of growth appears to be
identical. The method call I am doing is our troublesome open file call
(the object opens a proprietary data structure from the file system and
controls member access) and the file I am opening is about 900K. I do
this 1000 times and private bytes is 940,000K at the end in both
instances (give or take 20,000K). I can do workarounds to get size to
shrink but we need this thing to go away completely. Any further
thoughts?
 
I instantiated our object in both thread states and the results were
nearly identical. The private bytes grew consistently through the loop.
This COM object opens a file off the file system and controls access to
members of the contained data structure. The file I am opening is about
900K. I open it 1,000 times and memory usage at the end is 940,000K. I
can do a workaround to relieve the object of most of the file data in
which case the object only grows by about 3,000K from initial bytes to
final. The problem is that we could easily run this object 50,000 times
a day so even a small memory leak is going to add up quickly. Is it
simply rewrite time or is there a way to force this object to be
obliterated regardless of its state?
 
Ok, that proves there is something wrong with the COM object itself. Did you
check the build options (VB6 project) are you sure this is a STA object
(apartment threaded in VB6) and not a Single threaded object.
You can verify this using oleview.exe or directly in the registry (search
your COM object class and verify the threadingmodel attribute). If it's
marked 'Single' (or not marked at all) you'll need to re-build it as
'apartment' threaded.

Willy.
 
Thanks for all your help with this. The object is indeed Apartment
threaded. I ran the VS.NET 2003 upgrade wizard on a branch of the
source and it has quite a few conversion issues. This is pretty old
school VB 5-6 code with some API goodies (As Any) some ObjPtr issues as
well as some collection and compositions issues. It looks like it works
well under VB6 (similar loop tests showed stable memory usage) so the
problems seem to be strictly related to its use in .NET. From what I
have read that should not be the case though so I am a bit baffled. One
test I did in .NET was to create instances that grow the memory to 10x
my page file. This was interesting as it will chug along up to the
point of no memory then pause and garbage collect down to a reasonable
level. Unfortunately that is not acceptable for a server app so I need
to find a way to get this memory usage straightened out. A rewrite
looks daunting right now I would hate to undertake it if the problem
lies elsewhere.
 
Borco said:
Thanks for all your help with this. The object is indeed Apartment
threaded. I ran the VS.NET 2003 upgrade wizard on a branch of the
source and it has quite a few conversion issues. This is pretty old
school VB 5-6 code with some API goodies (As Any) some ObjPtr issues as
well as some collection and compositions issues. It looks like it works
well under VB6 (similar loop tests showed stable memory usage) so the
problems seem to be strictly related to its use in .NET. From what I
have read that should not be the case though so I am a bit baffled. One
test I did in .NET was to create instances that grow the memory to 10x
my page file. This was interesting as it will chug along up to the
point of no memory then pause and garbage collect down to a reasonable
level. Unfortunately that is not acceptable for a server app so I need
to find a way to get this memory usage straightened out. A rewrite
looks daunting right now I would hate to undertake it if the problem
lies elsewhere.

Well if it's really 'Apartment' threaded it should not behave like that, but
it behaves exactly like that when it's marked "Single" threaded in the
registry.
To simulate the issue, you could create a small COM object in VB6 and set
it's threading behavior to Single, and run the same test as before, you'll
notice the same (bad) behavior, change the threading to 'Apartment' and
you'll notice the correct behavior.
One other thing you could try is to release the COM object reference through
a call to Marshal.ReleaseComObject(comRef).

Willy.
 
I have used OLEView as well as looking directly at the registry and it
is most definitely Apartment threaded. We are using ReleaseComObject
but still seeing some growth. The frustrating point is that internally
the writers are saying it works based on VB6 behavior which is true but
it behaves differently when wrapped in an interop wrapper. Oh well, we
will go back to searching for a solution...
 
Some growth? How much and what exactly. I would like to see some figures
like the Private byte growth after a few million instantiations. Are you
sure the memory consumption never drops?

Willy.
 
What we are unsure of is what we should expect from the garbage
collector. We are seeing growth on the order of 4K per instance.
Running a million iterations is not realistic as this particular test
uses 1 meg of data and appears to be the most effective test. We are
taking another route for a short time to try to pinpoint the problem. I
will get back with our results.
 
Back
Top