iteye_1683 2008-12-11 17:57
浏览 460
已采纳

如何卸载assembly?或者class

.net的动态编译功能很好,但是有个问题,动态编译的代码,每次执行后,都会产生一个新的assembly,而且无法卸载。这个动态方法执行多次之后,就会慢慢的内存泄漏。因为每个assembly都会占用内存。
google了一下,没有发现满意的答案。唯一的答案就是,把assembly放到一个appdomain里,然后所在的appdomain卸载掉。但如果这样的话,所有的东西都得通过rpc调用了,效率非常低。
从原理上来说,已经加载的class,应该是可以卸载的。不知道是否有办法调用原生的api去卸载一个已经加载的assembly。
[b]问题补充:[/b]
我的问题是,需要实现一个脚本操作的功能,对已有的数据进行处理,并且用到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。我以为,动态的创建和销毁代码,也是非常必要的。它实际上是未来编程语言的一个方向。
[b]问题补充:[/b]
再次感谢RednaxelaFX兄的热情解答。使我明白DLR是怎么实现的。
ironpython是个好主意。可惜我要的不仅仅是脚本,还需要linq的查询结果。如果用ironPython的话,它返回给我的是个动态类型,虽然可以动态增加删除成员,奈何我还要对返回的数据进行处理。如果不是.net的数据控件支持的类型,我还得开发一堆数据控件或者adapter去支持它的类型,比较麻烦,我还没做好成为ironpython的开发人员的心理准备。最理想的办法,就是能卸载assembly。这样工作量就是最小的。其实,不卸载也是无所谓的,因为我可以把写好的脚本缓存起来,不必每次都生成新的assembly。客户也不会无聊到写很多脚本故意把它搞崩溃。
之所以研究这个问题,纯属心理上的洁癖,想让它有个更好的解决方案。
关于.net保护工具如何让已经加载的类重新被卸载的问题,我也不知道答案,作者也没有公布。因为这可能就是他保护技术的核心。我想,如果有办法能让程序回到一个程序集加载之前的状态,就算是完成了卸载操作。如果确信程序集不再被引用,那么应该有办法完成这个操作。不过,这一定需要非常了解clr的底层结构。
.net没有提供加载的类卸载的方法,只是出于安全性的理由,这个类可能被别的代码引用,卸载了它,就可能会出现野指针。
此外,如果类可以卸载,那么有些静态构造函数的语义,可能会被改变。因为重新加载类的时候,静态构造函数可能会再次执行。
如果类也增加引用计数之类的机制,那么,类本身也应该是可以被gc的。
既然代码可以动态的创建,那为什么不可以动态的删除?既然对对象的GC可以做到,那么对类型的GC,一样可以做到。这不过是编程语言设计者观念上的问题,不是不可逾越的障碍。
静态编译,不过是为了提高运行效率而已。如果效率可以接受,那静态编译对于编程思想的表达,并不是必须的。
也就是说,动态的产生类,动态的销毁类,完全是可行的。类也不过是数据而已。

  • 写回答

2条回答 默认 最新

  • rednaxelafx 2008-12-11 18:29
    关注

    不,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可以参照之前我写的[url=http://rednaxelafx.iteye.com/blog/237822]一些帖子[/url]看看。

    如果你是用CodeDOM来做动态编译,但实际上只是为了得到一个方法的内容,像是[url=http://rednaxelafx.iteye.com/blog/182759]这个例子[/url]里的那样,那么最好还是转用LCG或者Expression tree。
    如果真的需要用到类(因为需要实现某些接口或者继承某个基类),那就只能用AppDomain的workaround了。
    如果说并不是要实现接口或继承类,只是想让几个动态生成的方法共享一些变量,那可以考虑通过闭包来实现变量的共享。
    如果LINQ的Expression tree功能不够,例如说需要赋值、循环等功能的话,可以考虑使用DLR中的Expression tree。DLR是以Ms-PL许可证开源的,基本上没什么使用限制。它的源码可以从[url=http://www.codeplex.com/dlr]CodePlex[/url]获得。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器