关于内存的一些bug
从内存管理方面来讲,C#是个完美的天堂,你不用像在C++中那样万分小心的管理着你的内存;尽管如此,本人还是喜欢C++要多些,正如微软称C++为native代码 - C++对我来讲,的确是要更native一些。
其实,很多时候,C++之于C#,就像手动挡之于自动挡。
闲话不扯,一般情况下,我们会碰到的内存问题有使用未初始化的指针,使用空指针,释放已经释放过的内存等等。最近工作中也遇到两个内存相关的问题,觉得记下来还是有些价值的。
谁分配,谁释放
一个第三方提供的类的对象,每次析构的时候都会crash。仔细检查后发现,我们在此之前new了一块内存并将指针设给了该对象的一个成员(object.p)。于是怀疑该类在析构函数中试图delete该块内存。 试了一下在析构之前先将Object.p置0, 问题果然不再存在,于是基本可以确定是这个原因了。
这里其实有两个问题,一是我们的代码没有释放自己分配的内存;二是第三方库的代码,不应该delete不是自己分配的内存。谁分配,谁释放是一个相当重要的原则,一个试图释放使用者new出来的内存的第三方库,至少存在以下问题:
- 一般情况下,使用着会释放自己new出来的内存,那么这里就会出现二次释放的问题。
- 如果任何一方重载了operator new和operator delete,定义了自己的内存池,那么基本就是鸡同鸭讲了。
- 如果传给你的是一个栈上的对象呢,你也delete?
无意中被破坏的内存
我们有一个基于内存的transaction manager,基本原理就是,每做一个数据修改就会将被修改过的内存备份起来,然后undo的时候,将备份的数据再覆盖回来。当然,这里需要被备份的内存,使用一种特殊的方式分配的,我们称之为transacted memory。考虑如下流程:
- 做一个数据修改,比如画一个圆
- 该操作需要将提供画圆函数的DLL动态加载进来
- 在该DLL加载及初始话化过程中,创建了一个transacted memory,比如说一个std::map
- 画圆完成后,做一个Undo操作
于是该新画出来的圆又被原内存覆盖回去了,而同时,在加载DLL过程中分配的那个std::map也被覆盖回去了,注意,加载DLL的过程并不会被undo,于是,当程序再次试图访问该map时出错,因为该map的内容已经完全面目全非了。
这个bug花了我4、5个小时才发现问题所在,解决方法其实也很简单,那就是避免在初始化DLL时分配transacted memory。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述