我的方案如下:
以下是我正在尝试使用的代码
class Program
{
static void Main(string[] args)
{
Evidence e = new Evidence(AppDomain.CurrentDomain.Evidence);
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("Creating new AppDomain");
AppDomain newDomain = AppDomain.CreateDomain("newDomain", e, setup);
string fullName = Assembly.GetExecutingAssembly().FullName;
Type loaderType = typeof(AssemblyLoader);
var loader = (AssemblyLoader)newDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap();
Console.WriteLine("Loading assembly");
Assembly asm = loader.LoadAssembly("library.dll");
Console.WriteLine("Creating instance of Class1");
object instance = Activator.CreateInstance(asm.GetTypes()[0]);
Console.WriteLine("Created object is of type {0}", instance.GetType());
Console.ReadLine();
Console.WriteLine("Unloading AppDomain");
instance = null;
AppDomain.Unload(newDomain);
Console.WriteLine("New Domain unloaded");
Console.ReadLine();
}
public class AssemblyLoader : MarshalByRefObject
{
public Assembly LoadAssembly(string path)
{
return Assembly.LoadFile(path);
}
}
}
library.dll 只包含一个虚拟类,带有一个巨大的字符串表(以便更轻松地跟踪内存消耗)
现在的问题是内存实际上没有被释放。更令人惊讶的是,在AppDomain.Unload()
之后内存使用率实际上会增加有人能解释一下这个问题吗?
答案 0 :(得分:7)
这不是一个完整的答案:我只是注意到你使用一个字符串作为有效载荷。字符串对此没有用,因为字符串是实例化的。 Interned字符串在AppDomains之间共享,因此卸载AppDomain时不会卸载该部分。尝试使用byte []代替。
答案 1 :(得分:6)
我发布了一个示例,其中3个不同的程序集在不同的应用程序域中加载并成功卸载。这是链接http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html
答案 2 :(得分:5)
回答我自己的问题 - 不知道是否有更好的方法在StackOverflow上做...如果有,我会感激指示...... 无论如何,通过互联网挖掘我发现了另一种解决方案,我希望它更好。 以下代码,如果有人发现任何弱点 - 请回复。
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
for(int i=0;i<10;i++)
{
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.DoCallBack(loadAssembly);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);
}
AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2");
appDomain2.DoCallBack(loadAssembly);
appDomain2.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain2);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
private static void loadAssembly()
{
string fullPath = @"E:\tmp\sandbox\AppDomains\AppDomains1\AppDomains1\bin\Debug\BigLib.dll";
var assembly = Assembly.LoadFrom(fullPath);
var instance = Activator.CreateInstance(assembly.GetTypes()[0]);
Console.WriteLine("Creating instance of {0}", instance.GetType());
Thread.Sleep(2000);
instance = null;
}
private static void appDomain_DomainUnload(object sender, EventArgs e)
{
AppDomain ap = sender as AppDomain;
Console.WriteLine("Unloading {0} AppDomain", ap.FriendlyName);
}
}
答案 3 :(得分:4)
这是一个迟到的答案,但是对于今后对此问题的任何看法都值得。我需要实现与此类似的东西,但是以动态代码编译/执行方式。 最好的方法是在一个单独的域中执行所有方法,即:远程域,而不是主AppDomain,否则应用程序内存将始终增加和增加。您可以通过远程接口和代理解决此问题。 因此,您将通过一个接口公开您的方法,您将获得主AppDomain中的实例,然后在远程域中远程执行这些方法,卸载新创建的域(远程域),使其无效,然后强制GC收集未使用的物体我花了很长时间调试我的代码,直到我放心,我必须强制GC这样做,它工作得很好。我的实施大部分来自:http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm。
//pseudo code
object ExecuteCodeDynamically(string code)
{
Create AppDomain my_app
src_code = "using System; using System.Reflection; using RemoteLoader;
namespace MyNameSpace{
public class MyClass:MarshalByRefObject, IRemoteIterface
{
public object Invoke(string local_method, object[] parameters)
{
return this.GetType().InvokeMember(local_method, BindingFlags.InvokeMethod, null, this, parameters);
}
public object ExecuteDynamicCode(params object[] parameters)
{
" + code + } } } ";// this whole big string is the remote application
//compile this code which is src_code
//output it as a DLL on the disk rather than in memory with the name e.g.: DynamicHelper.dll. This can be done by playing with the CompileParameters
// create the factory class in the secondary app-domain
RemoteLoader.RemoteLoaderFactory factory =
(RemoteLoader.RemoteLoaderFactory)loAppDomain.CreateInstance("RemoteLoader",
"RemoteLoader.RemoteLoaderFactory").Unwrap();
// with the help of this factory, we can now create a real instance
object loObject = factory.CreateInstance("DynamicHelper.dll", "MyNamespace.MyClass", null);
// *** Cast the object to the remote interface to avoid loading type info
RemoteLoader.IRemoteInterface loRemote = (RemoteLoader.IRemoteInterface)loObject;
if (loObject == null)
{
System.Windows.Forms.MessageBox.Show("Couldn't load class.");
return null;
}
object[] loCodeParms = new object[1];
loCodeParms[0] = "bla bla bla";
try
{
// *** Indirectly call the remote interface
object result = loRemote.Invoke("ExecuteDynamicCode", loCodeParms);// this is the object to return
}
catch (Exception loError)
{
System.Windows.Forms.MessageBox.Show(loError.Message, "Compiler Demo",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Information);
return null;
}
loRemote = null;
try { AppDomain.Unload(my_app); }
catch (CannotUnloadAppDomainException ex)
{ String str = ex.Message; }
loAppDomain = null;
GC.Collect();//this will do the trick and free the memory
GC.WaitForPendingFinalizers();
System.IO.File.Delete("ConductorDynamicHelper.dll");
return result;
}
请注意,RemoteLoader是另一个应该已经创建并添加到主应用程序和远程应用程序的DLL。它基本上是一个接口和工厂加载器。以下代码取自上述网站:
/// <summary>
/// Interface that can be run over the remote AppDomain boundary.
/// </summary>
public interface IRemoteInterface
{
object Invoke(string lcMethod,object[] Parameters);
}
naemspace RemoteLoader{
/// <summary>
/// Factory class to create objects exposing IRemoteInterface
/// </summary>
public class RemoteLoaderFactory : MarshalByRefObject
{
private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
public RemoteLoaderFactory() {}
/// <summary> Factory method to create an instance of the type whose name is specified,
/// using the named assembly file and the constructor that best matches the specified parameters. </summary>
/// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>
/// <param name="typeName"> The name of the preferred type. </param>
/// <param name="constructArgs"> An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor. </param>
/// <returns> The return value is the created object represented as ILiveInterface. </returns>
public IRemoteInterface Create( string assemblyFile, string typeName, object[] constructArgs )
{
return (IRemoteInterface) Activator.CreateInstanceFrom(
assemblyFile, typeName, false, bfi, null, constructArgs,
null, null, null ).Unwrap();
}
}
}
希望这有意义并有所帮助......
答案 4 :(得分:3)
.Net使用非确定性终结。如果你想看看内存是否下降,你应该做...
GC.Collect();
GC.WaitForPendingFinalizers();
...卸载后。此外,除非您需要强制收集(相当不可能),否则您应该允许系统自行收集。通常,如果您觉得需要在生产代码中强制收集,则通常由于不在IDisposable对象上调用Dispose或者不释放非托管对象而导致资源泄漏
using (var imdisposable = new IDisposable())
{
}
//
var imdisposable = new IDisposable();
imdisposable.Dispose();
//
Marshal.Release(intPtr);
//
Marshal.ReleaseComObject(comObject);
答案 5 :(得分:1)
每个程序集也会加载到主域中。由于您使用Assembly实例,因此您的主域将加载此程序集,以便能够分析其中的所有类型。
如果要阻止在两个域中加载程序集 - 请使用AppDomain.CreateInstance方法。
答案 6 :(得分:1)
实际上,上述答案的组合使我(我希望)正确回答: 我的代码现在如下:
AppDomain newDomain = AppDomain.CreateDomain("newDomain", e, setup);
string fullName = Assembly.GetExecutingAssembly().FullName;
Type loaderType = typeof(AssemblyLoader);
FileStream fs = new FileStream(@"library.dll", FileMode.Open);
byte[] buffer = new byte[(int)fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Assembly domainLoaded = newDomain.Load(buffer);
object loaded = Activator.CreateInstance(domainLoaded.GetTypes()[1]);
AppDomain.Unload(newDomain);
GC.Collect();
GC.WaitForPendingFinalizers();
我无法使用AppDomain.CreateInstance,因为它需要我不知道的Assembly.FullName - 库是动态加载的。
感谢您的帮助, Bolek。
答案 7 :(得分:1)