Microsoft KB Archive/171414

From BetaArchive Wiki
Knowledge Base


Article ID: 171414

Article Last Modified on 2/12/2007



APPLIES TO

  • Microsoft OLE 4.0, when used with:
    • Microsoft Windows NT 4.0
  • Microsoft Windows 2000 Standard Edition



This article was previously published under Q171414

SUMMARY

COM's automatic garbage collection mechanism enables objects in COM servers to reclaim their resource allocations and delete themselves in the event that all clients terminate abnormally. Implementation of this mechanism involves periodic pings sent from each client machine to each server machine. There are some situations where it might be desirable to turn off this pinging to create a "NOPING" COM object. This article discusses these situations and also shows a method by which a COM server can turn off pinging for its objects.

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
                

Keywords: kbdcom kbhowto KB171414