-
如何卸载assembly?或者class5
.net的动态编译功能很好,但是有个问题,动态编译的代码,每次执行后,都会产生一个新的assembly,而且无法卸载。这个动态方法执行多次之后,就会慢慢的内存泄漏。因为每个assembly都会占用内存。
google了一下,没有发现满意的答案。唯一的答案就是,把assembly放到一个appdomain里,然后所在的appdomain卸载掉。但如果这样的话,所有的东西都得通过rpc调用了,效率非常低。
从原理上来说,已经加载的class,应该是可以卸载的。不知道是否有办法调用原生的api去卸载一个已经加载的assembly。
问题补充:
我的问题是,需要实现一个脚本操作的功能,对已有的数据进行处理,并且用到linq查询,然后返回查询的结果。
查询的逻辑是未知的,由客户输入脚本来实现。查询的结果是一个二维表。但是希望查询结果返回的数据结构是自描述的。为了简化问题,就用了linq的匿名对象。
现在的做法,是用动态编译的方法,用CSharpCodeProvider动态编译客户输入的那段代码,产生一个CompilerResult,这个地方的问题的实质,是需要一个编译器。现在为了省事,就调用了c#的编译器,但是后果就是每次执行一个脚本,就会生成一个Assembly。长期执行下去,内存会慢慢变少。因此希望能将这个Assembley卸载。
Expresstion Tree比较麻烦,因为问题就是要做语法分析,如果已经成了Tree就不需要做了。
感谢RednaxelaFX兄的解答,我想xie卸载程序集还是有可能的。理由是:第一,有些.net代码保护工具,就可以让clr把已经加载的类卸载,防止别人dump整个程序集。第二,Dlr的类是动态生成的。如果无法卸载动态生成的类,等着它的必定是内存泄漏。
从编程语言的发展趋势看,总的趋势是越来越灵活。开始阶段,是针对硬件的汇编,然后是对硬件的具体实现有一定抽象作用的c语言,再到有oo特性的c++,然后是继承了GC和更过oo特性的java和c#。现在的趋势是,对编程语言本身进行编程,表现就是Aop和动态语言。
动态语言,它需要的除了动态编译以为,就是对把gc的特性扩展到类的类型本身。当一个类的代码不再被需要,它也可以被GC。我以为,动态的创建和销毁代码,也是非常必要的。它实际上是未来编程语言的一个方向。
问题补充:
再次感谢RednaxelaFX兄的热情解答。使我明白DLR是怎么实现的。
ironpython是个好主意。可惜我要的不仅仅是脚本,还需要linq的查询结果。如果用ironPython的话,它返回给我的是个动态类型,虽然可以动态增加删除成员,奈何我还要对返回的数据进行处理。如果不是.net的数据控件支持的类型,我还得开发一堆数据控件或者adapter去支持它的类型,比较麻烦,我还没做好成为ironpython的开发人员的心理准备。最理想的办法,就是能卸载assembly。这样工作量就是最小的。其实,不卸载也是无所谓的,因为我可以把写好的脚本缓存起来,不必每次都生成新的assembly。客户也不会无聊到写很多脚本故意把它搞崩溃。
之所以研究这个问题,纯属心理上的洁癖,想让它有个更好的解决方案。
关于.net保护工具如何让已经加载的类重新被卸载的问题,我也不知道答案,作者也没有公布。因为这可能就是他保护技术的核心。我想,如果有办法能让程序回到一个程序集加载之前的状态,就算是完成了卸载操作。如果确信程序集不再被引用,那么应该有办法完成这个操作。不过,这一定需要非常了解clr的底层结构。
.net没有提供加载的类卸载的方法,只是出于安全性的理由,这个类可能被别的代码引用,卸载了它,就可能会出现野指针。
此外,如果类可以卸载,那么有些静态构造函数的语义,可能会被改变。因为重新加载类的时候,静态构造函数可能会再次执行。
如果类也增加引用计数之类的机制,那么,类本身也应该是可以被gc的。
既然代码可以动态的创建,那为什么不可以动态的删除?既然对对象的GC可以做到,那么对类型的GC,一样可以做到。这不过是编程语言设计者观念上的问题,不是不可逾越的障碍。
静态编译,不过是为了提高运行效率而已。如果效率可以接受,那静态编译对于编程思想的表达,并不是必须的。
也就是说,动态的产生类,动态的销毁类,完全是可行的。类也不过是数据而已。2008年12月11日 17:57
2个答案 按时间排序 按投票排序
-
采纳的答案
不,CLR的设计本来就是加载进来的class是无法卸载的。所以很多managed/unmanaged混合的程序都是采用AppDomain的workaround来解决卸载问题的。
如果你需要动态编译的内容没有复杂到要用到类,而只是一些相对独立的方法的话,那LCG(Lightweight Code Generation)是你不二的选择。LCG最基本的用法是通过System.Reflection.Emit.DynamicMethod类获取ILGenerator,动态写出需要的代码,并由DynamicMethod得到对应的委托来执行后续的调用。DynamicMethod不需要与任何类相关联,可以被GC。
上面是比较原始的做法。在.NET 3.5增添了Expression tree(System.Linq.Expressions命名空间)之后,也可以通过Expression tree来替代直接使用反射来动态生成代码。关于Expression tree可以参照之前我写的一些帖子看看。
如果你是用CodeDOM来做动态编译,但实际上只是为了得到一个方法的内容,像是这个例子里的那样,那么最好还是转用LCG或者Expression tree。
如果真的需要用到类(因为需要实现某些接口或者继承某个基类),那就只能用AppDomain的workaround了。
如果说并不是要实现接口或继承类,只是想让几个动态生成的方法共享一些变量,那可以考虑通过闭包来实现变量的共享。
如果LINQ的Expression tree功能不够,例如说需要赋值、循环等功能的话,可以考虑使用DLR中的Expression tree。DLR是以Ms-PL许可证开源的,基本上没什么使用限制。它的源码可以从CodePlex获得。2008年12月11日 18:29
-
针对问题补充的回答补充:
跟我猜的差不多,果然是为了动态执行一些用户输入的表达式。用过LINQPad的话,你可能会对它的功能感到熟悉——它也是让用户输入C#代码作为查询依据,然后动态编译那个脚本并执行LINQ查询。它是如何实现的呢?稍微调查一下就可以发现,它也是使用CodeDOM(具体来说也就是CSharpCodeProvider)来编译代码,并且将生成的程序集放在别的AppDomain里的。这种做法虽然不彻底但一点都不罕见,呵呵。
针对这个问题的使用场景,我觉得可以推荐使用IronPython 2.0。既然只是需要执行用户输入的代码,用IronPython是再合适不过了;这就是脚本语言的主要应用场景之。它与.NET的互操作相当方便,而且Python本身的语法也比较简洁。整个代码编译过程都由IronPython来解决,GC什么的问题都不用操心。IronPython里生成的对象本身也是动态的,可以添加/删除成员来改变其数据的形状,应该够用了。
mooniscrazy 写道第一,有些.net代码保护工具,就可以让clr把已经加载的类卸载,防止别人dump整个程序集。
CLR是否能卸载单个程序集而不卸载整个AppDomain,我觉得这里说得非常清楚,不必重复解释。不支持这个功能是由理由的,我在学习SSCLI的时候也关注过。问题中提到的.NET代码保护工具的工作原理我倒不清楚,如果您有什么发现的话可以告诉我吗?mooniscrazy 写道第二,Dlr的类是动态生成的。如果无法卸载动态生成的类,等着它的必定是内存泄漏。
事实上DLR动态生成的“类”只有参数列表很长的Action/Func系委托类型。这里的“类”特指CLR类(也就是普通的.NET类)。
如果您有疑问:IronPython和IronRuby都是动态类型语言,难道它们的类都不是动态生成的?没错,它们就不是动态生成的CLR类型,而是更类似于“property bag”;那些动态类在原理上就像把成员名字与成员实例联系在一起的Dictionary<string,object>而已,并不是真正的动态生成CLR类型了。这些所谓的动态类确实可以被GC,就像一个已经没有活引用指向的Dictionary对象一样。
根据现有的设计,CLR支持动态生成和GC单独的方法(通过LCG;DLR内部同样是使用LCG的),但不支持卸载单独的类型。2008年12月12日 12:36
相关推荐
### C#中加载和卸载DLL #### 一、引言 在软件开发过程中,动态链接库(Dynamic Link Library,简称DLL)作为...同时,这种方法也适用于那些需要频繁加载和卸载不同版本DLL的场景,比如插件系统或者需要热更新的软件。
然而,在某些情况下,我们可能需要动态地卸载已加载的程序集,以便释放资源或者更新程序集。"c# UnloadNet_Assembly.zip"中的内容似乎与这个主题相关,探讨了如何在C#中实现.NET程序集的卸载。 在.NET Framework ...
不过,注意.NET Framework 4.5及以后版本中,直接卸载一个Assembly是困难的,因为可能会引发其他正在运行的代码依赖。在.NET Core和.NET 5及以上版本,可以使用AssemblyLoadContext来实现更灵活的加载和卸载。 2. *...
插件开发的核心在于创建一个独立的功能模块,该模块可以被主程序动态加载和卸载,以增强或修改原有程序的功能。在VB中,我们通常通过COM(Component Object Model)组件来实现插件系统。COM是一种二进制标准,允许...
在进行C#开发过程中,经常会有动态...需要注意的是,动态加载DLL需要谨慎处理,尤其是涉及到安全性、版本兼容性和卸载操作时。另外,确保在使用动态加载的方法时,DLL文件存在并且位于指定的路径下,否则会引发异常。
public class MyService : ServiceBase { public MyService() { this.ServiceName = "MyCustomService"; } protected override void OnStart(string[] args) { // 在这里写启动时执行的代码 } protected ...
在插件架构中,事件可以用来传递信息,让主程序知道插件何时完成了某些操作或者需要主程序进行下一步处理。以下是一些关键步骤来实现这一目标: 1. **定义事件接口**:在插件DLL中,我们需要定义一个接口,该接口...
key.CreateSubKey(@"shell\open\command").SetValue("", $"\"{Assembly.GetExecutingAssembly().Location}\" %1"); } } ``` **步骤3:处理URL请求** 当用户点击`myurl:`链接时,系统会查找注册表并启动指定的程序...
$assembly = [Reflection.Assembly]::LoadFile("C:\Path\To\Your\Service.exe") $serviceInstaller = New-Object System.ServiceProcess.ServiceInstaller $serviceInstaller.DisplayName = "My Custom Service" $...
完成任务后,记得使用`AppDomain.Unload`卸载AppDomain,以释放资源。 在ASP.NET环境中,确保正确配置权限非常重要,因为IIS可能会限制应用程序对文件系统和网络的访问。你可能需要调整Web.config中的安全性设置,...
在ASP.NET中,一个网页从用户请求到完全加载会经历多个阶段,包括初始化、加载、验证、呈现和卸载等。我们主要关注的是“开始加载”和“加载完成”这两个阶段。 1. **设计界面**:在Default.aspx页面中,添加一个...
Java应用程序可以通过Java Service Wrapper(JSW)或者Apache Commons Daemon(Jsvc)等工具转化为Windows服务。 1. **Java Service Wrapper (JSW)**:JSW 是一个开源工具,允许Java应用程序作为操作系统服务运行。...
这个框架极大地简化了服务的安装、卸载和管理过程,使得开发者无需深入理解Windows服务的底层细节。 首先,要使用Topshelf创建一个Windows服务,你需要在项目中引入Topshelf NuGet包。接着,你可以定义一个类,该类...
然后,你可以使用Pegasus库提供的工具类来安装、卸载或控制服务。例如,`Pegasus.ServiceControl`类提供了`Install`、`Uninstall`和`Start/Stop/Pause/Resume`等方法。安装服务的示例代码如下: ```csharp using ...
public partial class _Default : System.Web.UI.Page //继承自System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } } 上述代码为Default.apx.cs页面代码。从上述代码可以看出...
首先,我们需要理解Razor类库(Razor Class Library,RCL)。RCL是ASP.NET Core中的一种项目类型,它提供了UI组件和共享资源,如视图、控制器、模型和静态文件。在动态加载插件的上下文中,RCL可以被视为我们的...
<<page 1>> page begin==================== 目 目目 目 录 录录 录 第一部分 C#语言概述.4 第一章 第一章第一章 第一章 .NET 编 编 ... 比尔....这一天 微软公司正式推出了其下一代...