Here's the overall picture: The app starts and launches X STA
threads (X is variable depending on settings in the registry and the
number of CPUs on the machine.) There is also an MTA that is created
for particular objects, but does not come into play in this situation.
Each of those X STA threads will receive messages (round robin) from an
MSMQ. When one of the STAs receives a message from the MSMQ, it will
get the already cocreated .NET object, or if it hasn't been cocreated
it will cocreate it then. next, it will call one particular method on
the .NET object, and one of the parameters passed is the message that
was fetched. Then, the .NET object will look at the message and
determine what other .NET method needs to be called. It will call upon
the other method via an invoke. Within the invoked method (still
..NET,) many COM objects may be used (MOST STA, but rare situations
where freethreaded only ones are used), therefore, several RCWs may be
generated... could be zero, could be 500. When that function is
returned from, the results are returned back to the calling COM object,
a little more work is done, and then the thread goes back to listening
to the MSMQ for the next request.
Normally, there is no memory bloat. The problem comes when the process
is hit with a large number of requests on the MSMQ very very rappidly
(which is what it's designed to do.) If the X STA threads that are
spun up are limited to just 1 thread (via a change in the registry,)
then the problem doesn't happen. Everything gets released no matter
how many messages get thrown into the mix. If the number of threads is
more than 1, then we start running into the problem. Once the memory
bloat has started, every single request (that uses COM objects in the
..NET invoke) will causes additional bloat, which also supports that
the finalizer thread is blocked.
To verify your statement that everything is running on the same thread,
I went into debug and noted that they all, in fact, are running the
same thread. Also, to clear up potential confusion, in my tests, none
of the COM objects being created in the .NET invoke are going into the
MTA.
Now, just as I thought I was starting to understand the problem, I ran
this test yesterday afternoon: I ran a CurrentThread.Join(100) in
hopes that it would completely clear up any window messages that wanted
to free the COM objects. The bloat still occurs. So, when the thread
returns to the calling COM object, I had it assert any time that there
were pending message in the queue. it never asserts, but the bloat
still occurs. It's almost as though there is something else blocking
the finalizer thread. I don't think it's any particular COM object
that blocks when trying to release, because the bloat occurs when any
one of the tested COM objects is isolated. I've made it so that none
of the .NET objects I'm using are calling their own finalizer, so
unless Microsoft wrote it, there's nothing there.
As for your suggestion to let the client thread join the MTA, there are
a lot of dependencies upon being isolated in the STA. It would be like
saying, I want to own beach front property so i'll move the moon closer
to earth; there would be enormous overhead to changing the program to
do that, and there would be lots and lots of repercussions.
If you have any other suggestions, I would greatly appreciate them,
however, you've spent quite some time helping, and I would entirely
understand if you couldn't.