The Truth about. NET Objects and sharing them between AppDomains

Source: Internet
Author: User
Tags mscorlib hosting

From http://geekswithblogs.net/akraus1/archive/2012/07/25/150301.aspx

I had written already some time ago how big a. NET object was. John Skeet as also made a very detailed post about object sizes in. NET. I wanted to know if we can deduce the object size isn't by experiments (measuring) but by looking at the Rotor source code. There is indeed a simple definition of the object headers how big a. NET object minimally can be. A CLR object is still a (sophisticated) structure which are at an address that's changed quite often by the garbage Collec Tor.

Image

The picture above shows that every. NET object contains a object header which contains information about which thread in Which AppDomain has locked the object (means called monitor.enter). Next comes the Method Table Pointer which defines a managed type for one AppDomain. If the assembly is loaded AppDomain neutral this pointer to the type object would have the same value in all AppDomains. This basic building block of the CLR type system was also visible in managed code via Type.TypeHandle.Value which have INTPT R size.

\sscli20\clr\src\vm\object.h

//
The generational GC requires that every object is at least bytes
in size.
#define MIN_OBJECT_SIZE (2*sizeof (byte*) + sizeof (Objheader))
A. NET object has basically the this layout:

Class Object
{
Protected
Methodtable* M_pmethtab;

};
Class Objheader
{
Private
// !!! Notice:m_syncblockvalue *must* is the last field in Objheader.
DWORD M_syncblockvalue; The Index and the Bits
};

For x86 the minimum size is therefore bytes = 2*4+4. And for x64 it is bytes = 2*8+8. The Objectheader struct is padded with another 4 bytes in x64 which does add up to bytes for every object instance. The min_object_size definition has actually a factor the inside it whereas we would expect 8 as minimum empty OBJECT SIZE. The previous sentence does contain already the answer to it. It makes little sense to define empty objects. Most meaningful objects has at least one member variable of class type which is indeed another pointer sized member hence The minimum size of bytes bytes in x86/x64.

It's interesting to know that the garbage collector does isn't know anything about AppDomains. For him the managed heap does only consist of objects which has roots or not and does clean up everything which are not ro Oted anymore. I found this during the development of Wmemoryprofiler which uses dumpheap of Windbg to get all object references from the Managed heap. When I do access all objects found this-I got actually objects from the other AppDomains as well. And they did work! It is therefore possible to share objects directly between AppDomains.

Why would want? Well it's fun and your can do really dirty stuff with that. Do you remember this cannot unload assemblies from an AppDomain? Yes that's still true but what would you ever want to unload an assembly? Mostly because you were doing some dynamic code generation which would at some point in time dominate your overall memory C Onsumption If you load generated assemblies into your AppDomain. I have the seen this stuff many times for dynamic query generation. The problem is so if you load the dynamically created code into another AppDomain you need to serialize the data to the Other AppDomain as well because you cannot share plain objects between AppDomains. To serialize potentially much data across AppDomain was prohibitively slow and therefore people live with the restriction T Hat code Gen would increase the working set quite a lot. With some tricks you can now share plain objects between AppDomain and get unloadable code as well.


Warning:this following stuff well beyond the specs is it does work since. NET 2.0 up to 4.5.
Do not try this at work!

When you load an assembly to your (default) appdomain you'll load it only for your the current AppDomain. The types defined there is not shared anywhere. There is one exception though:the types defined in mscorlib be always gkfx between all AppDomains. The mscorlib assembly is loaded to a so called Shared Domain. This isn't a real AppDomain but simply a placeholder domain where all assemblies was loaded which can be shared between a Ppdomains. An assembly loaded to the Shared Domain is loaded therefore AppDomain neutral. Assemblies loaded AppDomain Neutral has one special behavior:

AppDomain neutral assemblies was never unloaded even when no ' real ' AppDomain is using them anymore.
The picture below shows in which scenarios assemblies is loaded AppDomain neutral (green) from the Shared Domain.

Image

The first one is the most common one. You load a assembly into the default AppDomain. This defaults to Loaderoptimization.singleappdomain where every assembly are compiled from scratch again while other Appdoma INS with no special flags is created. Only the basic CLR types located in mscorlib is loaded AppDomain neutral and always shared between AppDomains no matter W Hat (with. NET 1.1 This is not the case) flags is used.

If you create e.g. 5 AppDomains with the default settings you'll load and JIT every assembly (except mscorlib) again and Get different types for each AppDomain although the were all loaded from the same assembly in the same loader context.

The opposite is loaderoptimization.multidomain where every assembly is loaded as AppDomain neutral assembly which ensures That all assemblies loaded on any AppDomain which has this attribute set is loaded and jited only once and share the Typ ES (=same Method Table pointer) between AppDomains.

An interesting hybrid are loaderoptimization.multidomainhost which does load only these assemblies AppDomain neutral which is loaded from the GAC. That means if you load the same assembly one time from the GAC and a second time the same one unsigned from your probing P Ath you won't get identical types but different Method Table pointers for the types.

Since we know that the GC does not know anything about AppDomains we (at least the managed heap objects does not contain INF Os in which AppDomain they reside) we should is able to pass the object via a pointer to another AppDomain. This could come in handy if we generate a lot of code dynamically for each query which was made against a data source but W E want a through-get rid of the compiled code by unloading the Query AppDomain from time to time without the normally requi Red cost to copy the data to is queried every time from the Default AppDomain into the Query AppDomain.

Image

You can see the "in action" in the sample application appdomaintests which are part of the test suite for Wmemoryprofiler. Here are the code for the main application which does create AppDomains in a loop send data via an IntPtr to another appdom Ain and get the calculated result back without passing the data via marshalling by value to the other AppDomain.

' Class program
{
<summary>
Show How to pass a object by reference directly into another AppDomain without serializing it in all.
</summary>
<param name= "args" ></param>
[Loaderoptimization (Loaderoptimization.multidomainhost)]
static public void Main (string[] args)
{
for (int i = 0; i < 10000; i++)//Try it often to see how the AppDomains do behave
{
To load our assembly AppDomain neutral we need to use multidomainhost on our hosting and child domain
If not we would get different Method tables for the same types which would result in invalidcastexceptions
For the same type.
Prerequisite for MultiDomainHost is, the assembly we share the data is
A) installed into the GAC (which requires as strong name as well)
If you would with MultiDomain then it would work but all AppDomain neutral assemblies would never be unloaded.
var other = Appdomain.createdomain ("Test" +i.tostring (), AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
Loaderoptimization = Loaderoptimization.multidomainhost,
});

Create Gate object in other AppDomain
Domaingate gate = (domaingate) other. Createinstanceandunwrap (assembly.getexecutingassembly (). FullName, typeof (Domaingate). FullName);

Now lets create some data
Crossdomaindata data = new Crossdomaindata ();
Data. Input = Enumerable.range (0, 10). ToList ();

Process it in the other AppDomain
Domaingate.send (gate, data);

Display result calculated in other AppDomain
Console.WriteLine ("Calculation in other AppDomain got: {0}", data. Aggregate);

AppDomain.Unload (other);
Check in debugger now if UnitTests.dll have been unloaded.
Console.WriteLine ("AppDomain unloaded");

}
}`
To enable code unloading in the other AppDomain I do use loaderoptimzation.multidomainhost which forces all non GAC Assem Blies to be unloadable. At the same time we must ensure, the assembly that defines crossdomaindata are loaded from the GAC to get a equal Meth Odtable pointer accross all AppDomains. The actual magic does happen in the Domaingate class which have a method dosomething which expects not a CLR object but the Object address as parameter to weasel a plain the CLR reference into another AppDomain. This sounds highly dirty and it certainly are but it's also quite cool;-).

//<summary>
//enables sharing of data between appdomains as plain objects without any marsalling Overhea D.
///</summary>
class Domaingate:marshalbyrefobject
{
///<summary>
//Operate on a P Lain object which is a shared from another AppDomain.
//</summary>
//<param name= "Gccount" >total number of Gcs</param>
//<param name= "O Bjaddress ">address to managed object.</param>
public void dosomething (int gccount, IntPtr objaddress)
{
if (gccount! = Objectaddress.gccount)
{
throw new NotSupportedException ("During The call a GC did happen. Please try again. ");
}

If you get a exception here disable under Projces/debugging/enable Visual Studio Hosting Process
The AppDomain which is used there seems to use Loaderoptimization.singledomain
Crossdomaindata data = (crossdomaindata) ptrconverter<object>. Default.convertfromintptr (objaddress);

Process input data from the other domain
foreach (var x in data). Input)
{
Console.WriteLine (x);
}

Otherassembliesusage user = new Otherassembliesusage ();

Generate output data
Data. Aggregate = data. Input.aggregate ((x, y) = + x + y);
}

public static void Send (Domaingate Gate, object o)
{
var old = Gcsettings.latencymode;
Try
{
Gcsettings.latencymode = Gclatencymode.batch; Try to keep the GC out of our stuff
var addandgccount = objectaddress.getaddress (o);
Gate. DoSomething (Addandgccount.value, Addandgccount.key);
}
Finally
{
Gcsettings.latencymode = old;
}

}


}
to get a object address I use Marshal.unsafeaddrofpinnedarrayelement and then try to work around the many R Ace conditions this does impose. But it isn't as bad are it sounds since you does need only to pass an object via a pointer once into the other AppDomain and Use the As data gateway to exchange input and output data. This is the can pass data via a pointer to another AppDomain which can is fully unloaded after you were done with it. The code unloadable I need to use Loadoptimization.multidomainhost for all AppDomains. The data exchange type is located in another assembly which are strong named and you need to put it into the GAC before you Let the sample run. Otherwise It would fail with this exception

Unhandled Exception:System.InvalidCastException: [A]appdomaintests.crossdomaindata cannot is cast to [B] Appdomaintests.crossdomaindata. Type A originates from ' Strongnameddomaingatedll, version=1.0.0.0, Culture=neutral, publickeytoken=98f280cda3cbf035 ' In the context ' Default ' at location ' C:\Source\WindbgAuto\bin\AnyCPU\Release\ StrongNamedDomainGateDll.dll '. Type B originates from ' Strongnameddomaingatedll, version=1.0.0.0, culture=neutral, publickeytoken=98f280cda3cbf035 ' The context ' Default ' at location ' C:\Source\WindbgAuto\bin\AnyCPU\Release\StrongNamedDomainGateDll.dll '.
At AppDomainTests.DomainGate.DoSomething (Int32 gccount, IntPtr objaddress) in C:\Source\WindbgAuto\Tests\ Appdomaintests\domaingate.cs:line 24
At AppDomainTests.DomainGate.DoSomething (Int32 gccount, IntPtr objaddress)
At AppDomainTests.DomainGate.Send (Domaingate Gate, Object o) in C:\Source\WindbgAuto\Tests\AppDomainTests\ DomainGate.cs:line 50
At AppDomainTests.Program.Main (string[] args) in C:\source\windbgauto\tests\appdomaintests\program.cs:line 41

At first it looks a little pointless to deny a cast to an object which is loaded in the default loader context for the VE Ry same assembly. But we did know now, the Method Table pointer for Crossdomaindata is different between the AppDomains. When you install the assembly into the GAC (is sure to use the. NET 4 gacutil!), the error goes away and we then get:

0
1
2
3
4
5
6
7
8
9
Calculation in other AppDomain got:45

Which shows that we can get data and is able to modify it directly between AppDomains. If You use this code in production and it does break. I have warned for you. This was far from beyond what the MS engineers want us to does and it can break the CLR in subtle unintended ways I has not found yet. Now there is got (hopefully) a much better understanding how the CLR type system and the managed heap does work. If questions is left. Start the application and look at! DumpDomain and! Dumpheap–stat and its related commands-see for yourself.

The Truth about. NET Objects and sharing them between AppDomains

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.