C#委托和闭包实现方式
在底层,编译器会为委托生成一个类,这个类包含了每一个闭包所捕获的变量作为它的一个公有字段。这也是为什么闭包捕获的变量的生命周期和委托的一致的原因。
具体可以看:https://blog.csdn.net/zhudaokuan/article/details/113032690
总的来说,C#中的委托和闭包的底层原理都与编译器如何处理这两种结构有关。编译器会为委托和闭包生成特殊的类和方法,以实现它们的功能
扩展:lua的闭包
lua闭包:https://www.cnblogs.com/mcyushao/p/17478562.html
lua闭包是直接在函数原型中建一个数组,存闭包(被捕获的变量)
如何保证闭包捕获的变量不会被释放?
1.lua在栈中建了一个容器用来存所有的闭包(让所有闭包都有引用,函数原型中的闭包数组存的是这个容器的key)。
2.c#将所有被捕获的变量封到一个匿名类中(值类型装箱),构建实例并找个东西引用着(引用类型只要有人引用就不会被释放)。这个实例在闭包执行完毕后消除引用并回收。
如何避免闭包导致的内存泄漏
1.及时释放引用:在不再需要闭包时,手动将引用置为null,这样可以解除对外部作用域的引用,帮助垃圾回收机制回收内存。
2.将需要保持引用的变量进行拷贝:如果闭包只需要引用外部函数中的某个变量,而不是整个作用域,可以将该变量进行拷贝,避免对整个作用域的引用。
3.避免循环引用:闭包中的内部函数如果引用了外部函数的变量,而外部函数的变量又引用了闭包中的内部函数,就会形成循环引用,导致内存泄漏。要避免循环引用,需要注意变量的引用关系,确保没有形成闭环。
例子:
原文:由于闭包引起的内存泄漏 - tiancaiKG - 博客园 (cnblogs.com)
此时closureTest已经被_tick捕获了,在这种情况下只能通过设置 _tick = null 才能解除引用, 最后才能释放对象,如果被捕获的是某资源(Texture2D)的引用,那么即使调用Resources.UnloadUnusedAssets();同样无法释放资源。
在引用成员变量的时候可以这样规避GC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private void Call( int value, System.Action< int > call) { call.Invoke(value); } private void Call(System.Action call) { call.Invoke(); } private int aa = 100; // 成员变量 void Update() { // 0B GC Call(aa, (_val) => { if (_val > 100) { } }); // 104B Call(() => { if (aa > 100) { } }); } |
总结来说就是回调函数之类的传入代理作为参数的情况,都有可能造成GC,并且大多数都有规避的方案,卫衣难处理的是代理中被引用的资源的问题,在你觉得资源已经没有被引用而调用UnloadUnusedAssets的时候无法清除资源,而即使调用GC也不会立即出发GC回收代理对象而解除资源的引用的时候会很郁闷。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了