从其它平台迁移而来


事件

计划做一个插件式的桌面应用框架,一方面练练手,另一方面算是自身的技术积累吧。

在练手过程中,发现一个巨难受的问题,dll卸载不掉,程序直接假死!

虽然可以不直接调用卸载,依赖主程序退出时卸载的特性,但做为插件式应用,必须能在运行中加载/卸载才算完整。即便不是插件式应用,dll的正常卸载也应该是很常用的功能,现在不正常,那么一定是代码写得有问题。

写了测试Demo,一行一行加代码,结果都能正常卸载,这就杯具了……测试了无数次,直到想把每一步都输出到日志时,dll无法卸载了。一点点分析源码后,发现很可能是日志中为了方便使用,加的initialization节和finalization节导致的,注释掉之后就真的正常了。

分析

initialization节应该是在Application.Initialize;时执行的,finalization节应该是在Application.Terminate;之后的某个时间点执行的(具体执行时机没深入研究过)。而我的dll是要做成插件的,不可避免会有可视化窗体,所以Application.Initialize;不可避免(Lazarus是这样,Delphi不是),而在initialization节中创建了日志记录器实例,在finalization节中进行释放,这样,在卸载dll时就出现了锁死的情况:卸载时有内存(实例)未释放,需要等内存释放了才能完全卸载,而未卸载又导致执行不到finalization节,就不能释放实例……于是,dll无法卸载,程序就进入假死状态。

以上只是初步分析,鉴于对底层机制了解不深,可能分析不完全正确,甚至是错误的。

结论

  • 在编写的dll中不要在initialization节和finalization节中进行内存管理的工作。

对于无对象、纯函数式的dllinitialization节和finalization节会不会产生影响尚未测试,目前也暂无这方面需求,待以后遇到了再详细测试吧。