All Your Base Are Belong To Us

Mostly .NET internals and other kinds of gory details

AppDomains and Remoting Life-Time Service

Sometimes we forget that .NET AppDomains are really separate light-weight processes inside a single .NET process.  We enjoy all the benefits of a light-weight process: fault isolation, assembly "unloading", custom security policies, and many other things.

But we often forget that AppDomains are separate from their host and from each other, and that under the covers they use good old .NET Remoting to communicate.  With that in mind, perhaps you can answer the following question:

I'm creating an object in a separate AppDomain and storing a reference to it in a dictionary.  At some point, when I'm trying to access the object I'm getting an exception as if the object has been collected - even though I have a reference to it.  What's going on?

Well, if you remember, when you create a remote object (including an object in a separate AppDomain), it can either be a marshal-by-value or a marshal-by-ref object.  If it's serializable, it's automatically treated as marshal-by-value, in which case you have a copy of the object in the "host" domain and there are no lifetime issues to consider.  But if the object derives from MarshalByRefObject, then it's deemed marshal-by-ref, meaning that you actually have a remote proxy to the original object, which lives in a separate AppDomain.

As such, the remote object's lifetime is no longer tied to the standard .NET GC rules.  It is tied to the remoting lease and sponsor mechanism.  This means that unless we explicitly opt-in and manage the object's lifetime, it's possible for it to appear collected and disappear even though we still have a reference to its proxy.  If this happens, the exception will resemble the following:

System.Runtime.Remoting.RemotingException: Object '/76e7cd41_2cd2_4e89_9c03_fae752ec4d59

/zb_uualy_cm6kwizjlentfdl_3.rem' has been disconnected or does not exist at the server.

(Note that the object URI is automatically generated when you use methods such as AppDomain.CreateInstanceAndUnwrap to create an object within the other domain.)

If we desire to achieve singleton semantics for the remote object, it's simplest to ensure that it never dies.  This can be done by overriding the InitializeLifetimeService method on your MarshalByRefObject-derived class and returning null instead of a valid ILease implementation.

Comments

Dew Drop - July 19, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - July 19, 2008 | Alvin Ashcraft's Morning Dew

# July 19, 2008 4:35 PM

pavely said:

I believe that fs an object is created in another AppDomain in the same process, as long as the returned proxy is alive - so is the object, regardless of the ILease implementation.

# July 20, 2008 4:38 PM

Sasha Goldshtein said:

Pavel, the following code example reproduces the issue - the proxy is alive (it's assigned to a static member), the proxy is to an object that is in another AppDomain in the same process, and yet after 5 minutes (it probably takes less than that) the object dies and you get a RemotingException.

   class RemoteClass : MarshalByRefObject

   {

       public string Echo(string s)

       {

           return s;

       }

   }

   class Program

   {

       static RemoteClass rc;

       static TimeSpan time;

       static Timer timer;

       static void Main(string[] args)

       {

           AppDomain ad = AppDomain.CreateDomain("OtherDomain");

           rc = (RemoteClass)ad.CreateInstanceAndUnwrap(

               Assembly.GetExecutingAssembly().FullName,

               typeof(RemoteClass).FullName);

           time = TimeSpan.FromMinutes(5);

           timer = new Timer((o) =>

           {

               time = time.Subtract(TimeSpan.FromSeconds(1));

               Console.WriteLine(time);

               if (time.TotalSeconds <= 1)

               {

                   Console.WriteLine(rc.Echo("Hello"));

                   timer.Dispose();

               }

           });

           timer.Change(0, 1000);

           Console.ReadLine();

       }

   }

# July 20, 2008 7:39 PM

alexandern said:

Whhhhattt???? Ooookay...

Not sure if this applies to me, but I had the following scenario

ASP.NET -> WCF Client -> Windows service Host -> WCF Service-> AppDomain -> PlugIn.HelloWorld();

and what you said.. seemed to have fixed the problem..

Thanks

# August 5, 2008 11:43 AM

Ariel's Uneditable Bliki said:

Sometimes I feel really stupid. You know, first-week-on-my-first-programming-job-ever stupid. Almost

# August 11, 2009 1:18 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: