对 精致码农大佬 说的 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裇衫上作适当的装饰,即可增添无穷的韵味.通过图案直接反映人类的精神风貌,你可以把日常生活中的兴趣.习惯.喜怒哀乐.嗜好等展露无疑,张扬个性.秀出自我.对于码 ...
随机推荐
- CSS浮动好文章
http://www.cnblogs.com/iyangyuan/archive/2013/03/27/2983813.html 看完上面这篇文章,我哭了.写的真好,我这块更菜.
- mdp文件-Chapter2-NVT.mdp
这是mdp文件系列的第二篇,介绍nvt平衡中要使用的mdp文件. 先上代码,nvt.mdp 1 title = OPLS Lysozyme NVT equilibration 2 define = - ...
- MySql Binlog 说明 & Canal 集成MySql的更新异常说明 & MySql Binlog 常用命令汇总
文章来源于本人的印象笔记,如出现格式问题可访问该链接查看原文 原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 目录 背景介绍 开启MySq ...
- 用system v消息队列实现回射客户/服务器程序
客户端程序 #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include< ...
- .net core中的哪些过滤器 (Authorization篇)
前言 咱们上篇说到,过滤的简单介绍,但是未介绍如何使用,接下来几篇,我来给大家讲讲如何使用,今天第一篇是Authorization.认证过滤器, 开发环境介绍 开发工具:VS2019 开发环境:.ne ...
- Python学习笔记5:模块/包
1.模块 模块简单理解就是一组功能的集合. 在Python中,一个文件(.py)就是一个模块,文件名即模块名. 模块的好处是大大提高代码的可维护性,其次,代码不必从零开始.当一个模块编写完毕,就可以被 ...
- 查询SQL Server数据库使用的版本号信息
如何查询当前连接服务器的数据库版本号,使用以下语句执行即可: select @@version
- IGH_Master主站配置驱动伺服电机和变频器总结
IGH_Master主站配置驱动伺服电机和变频器总结 Ethercat是倍福公司提出的一种工业现场总线协议,具有很好的实时性,IGH是一种开源的Ethercat主站实现协议,本文总结了一下使用IGH_ ...
- 超级无敌详细使用ubuntu搭建hadoop完全分布式集群
一.软件准备 安装VMware 下载ubuntu镜像(阿里源ubuntu下载地址)选择自己适合的版本,以下我使用的是18.04-server版就是没有桌面的.安装桌面版如果自己电脑配置不行的话启动集群 ...
- Guitar Pro 教程之有效的几种泛音
学习音乐的朋友们都知道,泛音的种类分为好几种,一般分为自然泛音和人工泛音,击弦泛音,拨片泛音,半泛音等等.本章节采用图文结合的方式讲解{cms_selflink page='index' text=' ...