对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考
一:背景
1. 讲故事
这段时间项目延期,加班比较厉害,博客就稍微停了停,不过还是得持续的技术输出呀! 园子里最近挺热闹的,精致码农大佬分享了三篇文章:
- 为什么要小心使用 Task.Run [https://www.cnblogs.com/willick/p/14078259.html]
- 小心使用 Task.Run 续篇 [https://www.cnblogs.com/willick/p/14100973.html]
- 小心使用 Task.Run 终篇解惑 [https://mp.weixin.qq.com/s/IMPgSsxTW0LGArfPP7rQXw]
核心代码如下:
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadLine();
}
static void Test()
{
var myClass = new MyClass();
myClass.Foo();
}
}
public class MyClass
{
private int _id = 10;
public Task Foo()
{
return Task.Run(() =>
{
Console.WriteLine($"Task.Run is executing with ID {_id}");
});
}
}
大意是:
Test()
方法执行完之后, myClass 本该销毁,结果发现Foo()
方法引用了 _id ,导致 GC 放弃了对 myClass 的回收,从而导致内存泄漏。
如果我的理解有误,请大家帮忙指正,挺有意思,评论区也是热闹非凡,总体看下来发现还是有很多朋友对 闭包
, 内存泄漏
,GC
等概念的认知比较模糊,同样作为技术博主,得要蹭点热度,这篇我准备从这三个方面阐述下我的认知,然后大家再回头看一下 精致 大佬的文章。
二:对闭包的认知
1. 什么是闭包
我最早接触闭包的概念是在 js 中,关于闭包的概念,懂得人自然懂,不懂的人得要挠会头,我准备不从概念而从代码入手,帮你梳理下,先看核心代码:
public class MyClass
{
private int _id = 10;
public Task Foo()
{
return Task.Run(() =>
{
Console.WriteLine($"Task.Run is executing with ID {_id}");
});
}
}
我发现很多人迷惑就迷惑在 Task.Run 委托中的 _id,因为它拿的是 MyClass 中的 _id,貌似实现了时空穿越,其实仔细想想很简单哈, Task.Run 委托中要拿 MyClass._id
,就必须把 MyClass 自身的 this 指针作为参数 传递给委托,既然有了这个this,啥值还拿不出来哈??? 遗憾的是 Run 不接受任何 object 参数,所以伪代码如下:
public Task Foo()
{
return Task.Run((obj) =>
{
var self = obj as MyClass;
Console.WriteLine($"Task.Run is executing with ID {self._id}");
},this);
}
上面的代码我相信大家都看的很清楚了,有些朋友要说了,空口无凭,凭什么你说的就是对的??? 没关系,我从 windbg 让你眼见为实就好啦。。。
2. 使用 windbg 验证
想验证其实很简单,用 windbg 在这条语句 Console.WriteLine($"Task.Run is executing with ID {_id}");
上放一个断点,命中之后看一下这个方法的参数列表就好了。
这句代码在我文件的第 35 行,使用命令 !bpmd Program.cs:35
设置断点。
0:000> !bpmd Program.cs:35
0:000> g
JITTED ConsoleApp4!ConsoleApp4.MyClass.<Foo>b__1_0()
Setting breakpoint: bp 00007FF83B2C4480 [ConsoleApp4.MyClass.<Foo>b__1_0()]
Breakpoint 0 hit
00007ff8`3b2c4480 55 push rbp
上面的 <Foo>b__1_0()
方法就是所谓的委托方法,接下来可以用 !clrstack -p
查看这个方法的参数列表。
0:009> !clrstack -p
OS Thread Id: 0x964c (9)
Child SP IP Call Site
000000BF6DB7EF58 00007ff83b2c4480 ConsoleApp4.MyClass.b__1_0() [E:\net5\ConsoleApp1\ConsoleApp4\Program.cs @ 34]
PARAMETERS:
this (<CLR reg>) = 0x0000025c26f8ac60
可以看到,这个方法有一个参数 this, 地址是: 0x0000025c26f8ac60
,接下来可以用 !do 0x0000025c26f8ac60
试着打印一下,看看到底是什么?
0:009> !do 0x0000025c26f8ac60
Name: ConsoleApp4.MyClass
MethodTable: 00007ff83b383548
EEClass: 00007ff83b3926b8
Size: 24(0x18) bytes
File: E:\net5\ConsoleApp1\ConsoleApp4\bin\Debug\netcoreapp3.1\ConsoleApp4.dll
Fields:
MT Field Offset Type VT Attr Value Name
00007ff83b28b1f0 4000001 8 System.Int32 1 instance 10 _id
观察上面输出,哈哈,果然不出所料,0x0000025c26f8ac60
就是 ConsoleApp4.MyClass
,现在对闭包是不是已经有了新的认识啦???
二:对内存泄漏的认识
1. 何为内存泄漏
英文中有一个词组叫做 Out of Control
,对,就是失去控制了,要想释放只能 自杀式袭击
了, 比如说:kill 进程,关机器。
好了,再回到这个例子上来,代码如下:
static void Test()
{
var myClass = new MyClass();
myClass.Foo();
}
当 Test 方法执行完成之后,myClass 的栈上引用地址肯定会被抹掉的, 有意思的是此时 Task.Run
中的委托方法肯定还没有得到线程调度,我又发现很多人在这一块想不通了,以为 内存泄漏
了。 对吧
对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考的更多相关文章
- 对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相
一:背景 1. 讲故事 昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为:[C#.NET 拾遗补漏]10:理解 volatile 关键字 (https://www.cnblogs.com/will ...
- Task.Run(), Task.Factory.StartNew() 和 New Task() 的行为不一致分析
重现 在 .Net5 平台下,创建一个控制台程序,注意控制台程序的Main()方法如下: static async Task Main(string[] args) 方法的主体非常简单,使用Task. ...
- 56岁潘石屹生日当天宣布要学编程语言Python,网友:地产商来抢码农饭碗了!
最近在码农界里,一个比较轰动的事情,就是地产大佬潘石屹,在56岁生日当天宣布要学习编程语言Python. 可能部分老铁不认识潘石屹,简单介绍下大佬背景: 潘石屹,1963年11月14日出生于甘肃天水, ...
- 6年DotNet码农的盲目经历
前言 第一篇没有选择记录与技术相关的文档,是考虑到有必要给查阅这篇文档的伙伴们“自我介绍”一下,大佬们看了求带或指导,我很愿意学习,初学者们看了千万不要重复走我之前的“学习之路”:我老家贵州,再过 ...
- 【整理】待毕业.Net码农就业求职储备
声明:本文题目来源于互联网,仅供即将从学校毕业的.Net码农(当然,我本人也是菜逼一个)学习之用.当然,学习了这些题目不一定会拿到offer,但是针对就业求职做些针对性的准备也是不错的.此外,除了技术 ...
- <开心一笑> 码农 黑客和2B程序员之间的区别
笔记本电脑 码农: 黑客: 2B程序员: 求2的32次方: 码农: System.out.println(Math.pow(2, 32)); 黑客: System.out.println(1L< ...
- 经典算法C++版(参考一线码农博文)
鉴于一线码农的算法博文基本通过C#完成,此处用C++再实现一遍,具体解法可参考其博文. 地址:http://www.cnblogs.com/huangxincheng/category/401959. ...
- [2013 eoe移动开发者大会]靳岩:从码农到极客的升级之路
(国内知名Android开发论坛 eoe开发者社区推荐:http://www.eoeandroid.com/) 前天,2013 eoe 移动开发者大会在国家会议中心召开,eoe 开发者社区创始人靳岩在 ...
- 专门为码农定制的14款创意的T裇(T-Shirt)设计
T裇衫是人们在各种场合都可穿着的服装,如在T裇衫上作适当的装饰,即可增添无穷的韵味.通过图案直接反映人类的精神风貌,你可以把日常生活中的兴趣.习惯.喜怒哀乐.嗜好等展露无疑,张扬个性.秀出自我.对于码 ...
随机推荐
- 深坑啊!同一个Spring AOP的坑,我一天踩了两次!
GitHub 18k Star 的Java工程师成神之路,不来了解一下吗! GitHub 18k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 18k Star 的Java工 ...
- leetcode 30day--1
Single Number Given a non-empty array of integers, every element appears twice except for one. Find ...
- Quirc二维码识别模块
背景 师兄要用这个参加集赛,但是说自己C语言不太行,花了一个多小时帮他分析了一下(虽然自己也不太行). 对应模块的GitHub地址:https://github.com/dlbeer/quirc. 记 ...
- 手把手教你5分钟从零开发一款简易的IDEA插件!项目经验/毕设不愁了!
我这个人没事就喜欢推荐一些好用的 IDEA 插件给大家.这些插件极大程度上提高了我们的生产效率以及编码舒适度. 不知道大家有没有想过自己开发一款 IDEA 插件呢? 我自己想过,但是没去尝试过.刚好有 ...
- 12.java设计模式之代理模式
基本介绍: 代理模式(Proxy)为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,想在 ...
- 从 Webpack 到 Snowpack, 编译速度提升十倍以上——TRPG Engine迁移小记
动机 TRPG Engine经过长久以来的迭代,项目已经显得非常臃肿了.数分钟的全量编译, 每次按下保存都会触发一次10s到1m不等的增量编译让我苦不堪言, 庞大的依赖使其每一次编译都会涉及很多文件和 ...
- CorelDRAW X7 X8 2017 2018是什么关系?
从CorelDRAW 2017版本开始我们叫习惯了的X几系列的CorelDRAW毅然决然的就换了称呼,所以有时候很多朋友对于软件版本,经常会傻傻分不清,还有人认为X8版本比2017版本高,究竟为什么会 ...
- FL Studio乐理教程之和弦进行
和弦级数 在一个调内,分别由调内7个音为根音组成的和弦总共有7个,每个和弦依次为1-7级和弦.例如在C大调内,以C为根音建立和弦,就是一级和弦,以D为根音建立和弦,即是二级和弦,以此类推. 图1:1- ...
- Cypress系列(93)- Cypress.dom 命令详解
如果想从头学起Cypress,可以看下面的系列文章哦 https://www.cnblogs.com/poloyy/category/1768839.html 作用 与 DOM 元素相关的 helpe ...
- leetcode 56合并区间 java
//先排序,将左区间小的放在前面,然后如果前一个的右区间大于下一个的左区间,则可以合并,分别用两个下标指向当前的大区间和将要考察的小区间 class Solution { public int[ ...