MORE INFORMATION
When a COM client terminates normally, it releases all references to its
server object. When a client terminates abnormally however, there might be
outstanding references to the server object. Without a garbage collection
mechanism, the server code has no way of knowing when to reclaim the
resources allocated for the COM object, which can then cause a resource
leak. To address this problem, COM implements an automatic garbage
collection mechanism in which the COM resolver process (RPCSS) on the
client machine pings the server machine on behalf of the client process.
A detailed description of the overall garbage collection (GC) strategy and
protocol of COM is beyond the scope of this article. There are, however,
many optimizations involved, such that the overhead of pinging even in a
very large-scale DCOM system is extremely low. The most basic optimizations
are (a) ping traffic occurs on a machine-to-machine basis, not on a client-
to-object basis or on a process-to-process basis; (b) all references from
all clients to all objects are summarized in a single 128-bit value (a
"ping set ID"), with only that 16-byte value sent across the network every
two minutes. In other words, in a steady state, one client holding one
reference to one object on a server will generate the same small amount of
ping traffic as 1,000 clients holding 10,000 references to 10,000 objects
on the same server. (A slightly larger ping message is sent to add or
remove objects from the ping set as the set of references varies over
time.) Moreover, alternatives to using COM's GC protocol (for example,
using periodic application-level "pings"--method calls that inform the
object that clients are still alive, or using an underlying transport
mechanism such as TCP keepalives) are demonstrably much less efficient.
Therefore, DCOM's default GC mechanism should be used for any objects that
must be shut down when their clients disappear or otherwise misbehave if
those objects would effectively become memory leaks on the server.
The resolver on the server machine keeps track of the pings for each server
object. The ping period is 2 minutes and, currently, it is non-
configurable. When the resolver on the server machine detects that an
object has not been pinged for 6 minutes, it assumes that all clients of
the object have terminated or otherwise are no longer using the object. The
resolver will then release all external references to the object. It does
this by simply having the object's stub manager (the COM runtime code that
delivers calls to each object) call ::Release() on the object's IUnknown
interface. At this point, the object's reference count will be zero so far
as the COM runtime is concerned. (There may still be references held by
local (same-apartment) clients, so the object's internal reference count
may not necessarily go to zero at this point.) The object may then shut
itself down.
NOTE: Garbage collection applies to all servers regardless of whether
their clients are local or remote, or a combination of local and remote.
The underlying pinging mechanism is different in the local case as no
network packets are generated, but for all practical purposes, the behavior
is the same.
There are some circumstances where it is desirable to turn off COM's
garbage collection mechanism. For example, you might have a stateless
object (for example, a multi-user computational object where all relevant
state is passed as [in] parameters on method calls and the relevant output
is provided as an [out] parameter) or a long-lived object with no per-
client-relative state (for example, objects that make up a directory of
other objects) that does not need the ability to detect the liveness of its
clients. Essentially, the kind of object under discussion is one in which
the internal implementation of the IUnknown::Addref and ::Release() methods
do not control the object's lifetime; some other mechanism is used to
determine when to shut down the object (if ever). In such situations, it
may be preferable to turn off the periodic pings to eliminate the network
traffic they cause. The following section shows you how to do this via
sample code.
It must be kept in mind that garbage collection is done on an object-by-
object basis and not on a process-by-process basis. So, if you have a
server process that serves multiple objects (including class factory
objects) to all clients and you do not want the server machine to be pinged
by its clients, then you must turn off pinging for all objects in the
server. As a corollary, while it is perfectly legitimate to disable pinging
for only one or some objects in a process or for all object in one or only
some processes on a server, the COM pinging protocol is so optimized that
there will be no significant change in the network bandwidth used by the
COM GC protocol whether there is one or 10,000 DCOM objects in use on a
machine-wide basis -- assuming all objects have approximately the same
number and set of clients. In other words, if there are any other DCOM
objects on a given server using COM's GC/pinging protocol, and those
objects are used by roughly the same number and set of clients that are
using an object that might be marshaled "NOPING", there is essentially no
advantage to turning off the pinging protocol with respect to that
particular object. There will only be a measurable decrease in network
traffic if the clients of the NOPING object(s) are significantly different
from the clients of the other objects on the same machine.
Also, it must be noted carefully that garbage collection is an all-or-
nothing proposition. If you turn off garbage collection for an object, then
COM-based lifetime management of the object is turned off also. This means
that clients can no longer call Release() on their interface pointers and
expect the object to receive the call. Release() called on any proxy is a
no-op for such objects. This may seem counterintuitive at first, but it is
the correct behavior because COM can no longer guarantee that the object
will be shutdown when clients disappear. If ::Release() was enabled in this
situation then the object would find that its reference count went to zero
when all clients behaved properly but never went to zero if any one client
misbehaved. This kind of totally unreliable behavior is far worse than the
simple rule that if objects are marshaled with the NOPING flag then without
exception they will never receive any ::Release() calls from the COM
runtime. As an implementation detail, to maximize network efficiency
::Release() calls on proxies for objects marshaled NOPING are never sent
across the wire.
Sample Code
You can turn off garbage collection and pinging for an object by marshaling
its interfaces with the MSHLFLAGS_NOPING flag. The following sample code
shows how to do this inside the class factory's CreateInstance() function.
This is required so that the standard marshaler object gets associated with
the stub manager for the object as soon as the object is created. Any
subsequent calls to CoMarshalInterface() by the stub manager will find the
pre-created standard marshaler object.
STDMETHODIMP
CClassFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID riid,
void**ppv)
{
HRESULT hr;
IMarshal *pIM = NULL;
IUnknown *pUnknown;
*ppv = NULL;
if (punkOuter != NULL)
return CLASS_E_NOAGGREGATION;
// create our object.
p = new CSimpleObject;
if (p == NULL)
return E_OUTOFMEMORY;
hr = p->QueryInterface(IID_IUnknown, (void**)&pUnknown);
hr = CoGetStandardMarshal(riid, pUnknown, 0, NULL, MSHLFLAGS_NOPING
|MSHLFLAGS_NORMAL, &pIM);
// don't release pIM. All marshals of all interfaces on this object
// from now on will automatically be NOPING
if (FAILED(hr))
{
Message(TEXT("Server: CoGetStandardMarshal Error"), hr);
return(E_FAILED);
}
pUnknown->Release();
hr = p->QueryInterface(riid, ppv);
return hr;
} // CClassFactory::CreateInstance