Solved

Need to Solve Memory Leak with CodeDom/Need to find out how to use AppDomain to unload Asslembly

Posted on 2007-12-04
14
1,856 Views
Last Modified: 2013-12-16
Hi,

I have the created the class shown in the code snippet from various sources around the net.   The code is supposed to give me a class that I can use to evaluate Javascript on the fly.

Sample usage is

private Evaluator eval = new Evaluator();
string x=eval.EvalJScript("(6*5)/3");

the variable x will get the value of 10.  as you can see, this is very useful for quite a number of things.   This code works very well to do what I need, however I am getting a problem.   Every Time I create a new Evaluator Class, its loading a new assembly into the app, and so after a while, a huge amount of memory is being leaked and wasted.  Considering I am using this from within an asp.net app, after a while the asp.net process uses too much memory and gets recycled.   What I need is to find a way to unload the assembly when I am finished with it.   I have been reading around the net and have discovered that you are supposed to create a seperate AppDomain, load the assembly created by CodeDom into this AppDomain, and then destroy this seperate AppDomain when finished to force the unload of the assembly.   My problem now is I have found no help on how to actually do this with my code below.   Can anyone help or give an example on how I may achieve this with my code.

Many Thanks,

Steed.
public class Evaluator

    {

        Type evaluatorType;

        object evaluator;
 

        public string EvalJScript(string JScript)

        {

            return evaluatorType.InvokeMember("Eval", BindingFlags.InvokeMethod, null, evaluator, new object[] { JScript }).ToString();

        }
 
 

        public Evaluator()  //class constructor

        {

            string JScriptSource =

            @"package Evaluator

            {

            class Evaluator

            {

            public function Eval(expr : String) : String

            {

            return eval(expr, ""unsafe"");

            }

            }

            }";
 

            CodeDomProvider compiler = CodeDomProvider.CreateProvider("JScript");

            CompilerParameters parameters = new CompilerParameters();

            parameters.CompilerOptions = "/t:library";

            parameters.GenerateInMemory = true;

            CompilerResults results = compiler.CompileAssemblyFromSource(parameters, JScriptSource);

            Assembly assembly = results.CompiledAssembly;

            evaluatorType = assembly.GetType("Evaluator.Evaluator");

            evaluator = Activator.CreateInstance(evaluatorType);

        }
 

    }

Open in new window

0
Comment
Question by:Steelsteed
14 Comments
 
LVL 3

Expert Comment

by:wizrr
Comment Utility
Have you looked to MSDN sample for that?
http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx

You can use sample, just derive your Evaluator from MarshalByRefObject and use it in CreateInstanceAndUnwrap.
0
 
LVL 1

Author Comment

by:Steelsteed
Comment Utility
Hi there, thanks for that info, however I am still unsure of how to apply this to the code I have.  Lets rephrase my skill on the subject, I am actually a noob, so I hope you dont mind if I ask for a helping hand.
I am unsure of how to use the MarshalByRefObject to get something to use with CreateInstanceAndUnwrap.   CreateInstanceAndUnwrap seems to want an assembly as a file on disk .. but in the current code my assembly is in memory. ... am not sure what exactly to do to glue things together ....
0
 
LVL 3

Expert Comment

by:wizrr
Comment Utility
Let's see.
//--
// Creates a new instance of the specified type. Parameters specify the assembly where the type is defined, and the name of the type.
//--
public Object AppDomain.CreateInstanceAndUnwrap (
      string assemblyName,
      string typeName
)
//--
//assemblyName
//The display name of the assembly. See Assembly.FullName.
//--
//typeName
//The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName property.



    

    public static string EvalJScript(string JScript) {

        // Construct and initialize settings for a second AppDomain.

        AppDomainSetup ads = new AppDomainSetup();

        ads.ApplicationBase = 

            System.Environment.CurrentDirectory;

        ads.DisallowBindingRedirects = false;

        ads.DisallowCodeDownload = true;

        ads.ConfigurationFile = 

            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
 

        // Create the second AppDomain.

        AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads);
 

        // Create an instance of MarshalbyRefType in the second AppDomain. 

        // A proxy to the object is returned.

        Evaluator mbrt = 

            (Evaluator) ad2.CreateInstanceAndUnwrap(

                typeof(Evaluator).Assembly.FullName, 

                typeof(Evaluator).FullName

            );
 

        // Call a method on the object via the proxy, passing the 

        // default AppDomain's friendly name in as a parameter.

        return ( mbrt.EvalJScript(JScript) );

    }
 

    public class Evaluator : MarshalByRefObject

    {

        Type evaluatorType;

        object evaluator;

 

        public string EvalJScript(string JScript)

        {

            return evaluatorType.InvokeMember("Eval", BindingFlags.InvokeMethod, null, evaluator, new object[] { JScript }).ToString();

        }

 

 

        public Evaluator()  //class constructor

        {

            string JScriptSource =

            @"package Evaluator

            {

            class Evaluator

            {

            public function Eval(expr : String) : String

            {

            return eval(expr, ""unsafe"");

            }

            }

            }";

 

            CodeDomProvider compiler = CodeDomProvider.CreateProvider("JScript");

            CompilerParameters parameters = new CompilerParameters();

            parameters.CompilerOptions = "/t:library";

            parameters.GenerateInMemory = true;

            CompilerResults results = compiler.CompileAssemblyFromSource(parameters, JScriptSource);

            Assembly assembly = results.CompiledAssembly;

            evaluatorType = assembly.GetType("Evaluator.Evaluator");

            evaluator = Activator.CreateInstance(evaluatorType);

        }

 

    }

Open in new window

0
 
LVL 3

Expert Comment

by:wizrr
Comment Utility
Oh. Forgot this:

AppDomain.Unload(ad2);

to unload your new app domain and free all resources and libs.
0
 
LVL 1

Author Comment

by:Steelsteed
Comment Utility
Hi There,

I have modded my code to look like the snippet below.  I changed things around a bit so as to make it fit in with the way I have been using the class in my project.
I am getting a problem however that the line
Eval mbrt =(Eval)ad2.CreateInstanceAndUnwrap(typeof(Eval).Assembly.FullName,typeof(Eval).FullName);

is making a System.IO.FileNotFoundException saying that the assembly cant be found.   Any ideas ?

Thanks,

Steed.
    public class Evaluator

    {

        public string EvalJScript(string JScript)

        {

            // Construct and initialize settings for a second AppDomain.

            AppDomainSetup ads = new AppDomainSetup();

            ads.ApplicationBase =Environment.CurrentDirectory;

            ads.DisallowBindingRedirects = false;

            ads.DisallowCodeDownload = true;

            ads.ConfigurationFile =AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

            

            // Create the second AppDomain.

            AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads);
 

            // Create an instance of MarshalbyRefType in the second AppDomain.  A proxy to the object is returned.

            Eval mbrt =(Eval)ad2.CreateInstanceAndUnwrap(typeof(Eval).Assembly.FullName,typeof(Eval).FullName);
 

            // Call a method on the object via the proxy, passing the 

            // default AppDomain's friendly name in as a parameter.

            string res = mbrt.EvalJScript(JScript);
 

            AppDomain.Unload(ad2);

            
 

            return res;

        }

    }
 

    public class Eval : MarshalByRefObject

    {

        Type evaluatorType;

        object evaluator;

 

        public string EvalJScript(string JScript)

        {

            return evaluatorType.InvokeMember("Eval", BindingFlags.InvokeMethod, null, evaluator, new object[] { JScript }).ToString();

        }

 

 

        public Eval()  //class constructor

        {

            string JScriptSource =

            @"package Evaluator

            {

            class Evaluator

            {

            public function Eval(expr : String) : String

            {

            return eval(expr, ""unsafe"");

            }

            }

            }";

 

            CodeDomProvider compiler = CodeDomProvider.CreateProvider("JScript");

            CompilerParameters parameters = new CompilerParameters();

            parameters.CompilerOptions = "/t:library";

            parameters.GenerateInMemory = true;

            CompilerResults results = compiler.CompileAssemblyFromSource(parameters, JScriptSource);

            Assembly assembly = results.CompiledAssembly;

            evaluatorType = assembly.GetType("Evaluator.Evaluator");        //this refers to the package and class in the jscript

            evaluator = Activator.CreateInstance(evaluatorType);

        }

 

    }

Open in new window

0
 
LVL 3

Expert Comment

by:wizrr
Comment Utility
Strange. Hmm. You can sign your assembly with key and register in GAC. This allows as reference and load it without depending to location of assembly.

Another way maybe, is to set

ads.ApplicationBase = ...;//Environment.CurrentDirectory;

to directory where your assembly (assembly where Eval is defined) is located. If that not helps - i don't know(
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 1

Author Comment

by:Steelsteed
Comment Utility
Hiya,

It seems the monsterous code is getting no simpler.   Let me explain what seems to be happening now.   This class I am using in inside a Class Library that is being used from my main asp.net app.  Now it seems that Environment.CurrentDirectory was actually bringing back the system32 folder and so the file was not being found.  I have set this to the folder where  the dll for this class lib is in.  Now instead of the System.IO.FileNotFoundException error I am getting a System.IO.FileLoadException, which seems to be because of an access denied.  The full error msg is

Could not load file or assembly 'hmsModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Access is denied.

hmsModel is the assembly name of the class library that this code is within.   So it seems that I cant load it again because its already in use ?   Any ideas ?   I would need to be able to load and unload multiple instances as far as I am aware.    From what I am reading I would actually need a different AppDomain for each and every assembly instance I load.  Is this right ?
0
 
LVL 1

Author Comment

by:Steelsteed
Comment Utility
it seems to be something to do with the fact that I am referencing the library in the project already.   I have tried moving the code to a separate library/assembly, however I still have to reference the lib in order do do the casting to the type Eval in the line

Eval mbrt =(Eval)ad2.CreateInstanceAndUnwrap(typeof(Eval).Assembly.FullName,typeof(Eval).FullName);

I am unsure if this is the real problem though

0
 
LVL 3

Expert Comment

by:wizrr
Comment Utility
>> So it seems that I cant load it again because its already in use ?
No. You just have limited access in your code. I think your site must be full trust, without restrictions. I think this is hosting problem. This problem also can occur if you have no NTFS access 'execute' rights or something.
>> I would need to be able to load and unload multiple instances as far as I am aware.    From what I am reading I would actually need a different AppDomain for each and every assembly instance I load.  Is this right ?
Yes. This is solution.
>> it seems to be something to do with the fact that I am referencing the library in the project already.
No. Maybe you need SecurityPermissionFlag.ControlAppDomain permission to create your own AppDomain.
0
 
LVL 1

Author Comment

by:Steelsteed
Comment Utility
So where and how would I tweak these security settings if that is the problem ?
I am running this off my XP development box, with the project setup in IIS 5.1
Remember that the library has to have execute rights for it to run the code inside it to begin with, and that is working.  Its just trying to load a new instance of the assembly into a different AppDomain that is failing.   If I were to somehow implement this SecurityPermissionFlag.ControlAppDomain permission, where would I code that ?  The NTFS permissions might be wrong, but I dont think so as the code is able to use this library/assembly quite fine by just referencing it.
0
 
LVL 3

Accepted Solution

by:
wizrr earned 500 total points
Comment Utility
I don't know how to do that through IIS. For any .NET assembly you can manage security through caspol tool (all assemblies must be signed):
http://msdn2.microsoft.com/en-us/library/cb6t8dtz(VS.80).aspx

But to verify that you have SecurityPermissionFlag.ControlAppDomain flag of  SecurityPermission class you need to apply this attribute to your assembly: [SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted=true)]. If your assembly will not be loaded by IIS - that means your assembly have no SecurityPermissionFlag.ControlAppDomain and other flags, and cannot load\create AppDomains.

This is not solution, source of your problem can be something another.

As solution try also to add your IIS running user to administrator group (if you testing your site at home\work you can do that). After that you can know what is problem actually.
0
 
LVL 1

Expert Comment

by:Computer101
Comment Utility
Forced accept.

Computer101
EE Admin
0
 

Expert Comment

by:BinShao1987
Comment Utility
good, use AppDomain can fix the MS Bug.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now