内存泄漏是否有可接受的限制
Is there an acceptable limit for memory leaks?
我刚刚开始在C++中试验 SDL,我认为定期检查内存泄漏可能是早期养成的好习惯。
考虑到这一点,我一直在通过 Valgrind 运行我的"Hello world"程序以捕获任何泄漏,尽管我已经删除了除最基本的SDL_Init()
和SDL_Quit()
语句之外的所有内容,但 Valgrind 仍然报告丢失了 120 个字节,仍然可以访问 77k。
我的问题是:内存泄漏是否有可接受的限制,或者我应该努力使所有代码完全无泄漏?
请注意,Valgrind 不会在其测量中发现误报。
内存分析器的许多天真实现将丢失的内存标记为泄漏,而实际上并非如此。
也许可以阅读维基百科关于Purify的文章的外部链接部分中的一些论文。我知道 Purify 附带的文档描述了几种情况,在这些情况下,您在尝试检测内存泄漏时会出现误报,然后继续描述 Purify 用来解决问题的技术。
顺便说一句,我与IBM没有任何关系。我刚刚广泛使用了Purify,并保证其有效性。
编辑:这是一篇涵盖内存监控的优秀介绍性文章。它是纯化特定的,但关于内存错误类型的讨论非常有趣。
呵呵。
干杯
抢
您必须小心"内存泄漏"的定义。在首次使用时分配一次并在程序退出时释放的东西有时会被检漏仪显示出来,因为它在第一次使用之前就开始计数。但这不是泄漏(尽管它可能是糟糕的设计,因为它可能是某种全局的)。
要查看给定的代码块是否泄漏,您可以合理地运行一次,然后清除检漏器,然后再次运行它(这当然需要对检漏器进行编程控制)。每次运行程序"泄漏"一次的事情通常无关紧要。每次执行时"泄漏"的事情通常最终都会很重要。
我很少发现在这个指标上达到零太难了,这相当于观察内存使用情况的缓慢变化,而不是丢失的块。我有一个库,它变得如此繁琐,有缓存和UI家具等等,以至于我只是运行了三次测试套件,并忽略了任何没有以三个块的倍数发生的"泄漏"。我仍然抓住了所有或几乎所有真正的泄漏,并在我摆脱了唾手可得的果实后分析了棘手的报告。当然,为此目的使用测试套件的弱点是(1)您只能使用其中不需要新过程的部分,以及(2)您发现的大多数泄漏都是测试代码的错误,而不是库代码...
生活在内存泄漏(和其他粗心大意的问题)中,最好的情况是(在我看来)非常糟糕的编程。 在最坏的情况下,它使软件无法使用。
您应该首先避免引入它们,并运行您和其他人提到的工具来尝试检测它们。
避免草率的编程 - 已经有足够多的糟糕程序员 - 世界不需要另一个。
编辑
我同意 - 许多工具可以提供误报。
如果你真的担心内存泄漏,你需要做一些计算。
您需要测试您的应用程序大约一个小时,然后计算泄漏的内存。这样,您将获得泄漏的内存字节/分钟值。
现在,您需要估计程序会话的平均长度。例如,对于记事本.exe,15 分钟对我来说听起来是一个很好的估计。
如果(平均会话长度)*(泄漏的字节/分钟)> 0.3 *(进程通常占用的内存空间),那么您可能应该做更多的努力来减少内存泄漏。我刚刚补了0.3,用常识来确定你可接受的阈值。
请记住,成为程序员的一个重要方面是成为软件工程师,而工程通常是从两个或多个糟糕的选项中选择最不糟糕的选项。当您需要衡量选项实际上有多糟糕时,数学总是派上用场。
大多数操作系统(包括Windows)将在卸载程序时返回程序的所有分配内存。这包括程序本身可能已丢失跟踪的任何内存。
鉴于此,我通常的理论是,在启动期间泄漏内存是完全可以的,但在运行时泄漏内存是不行的。
所以真正的问题不在于你是否泄漏了任何内存,而是你在程序运行时是否不断泄漏它。如果你使用你的程序一段时间,无论你做什么,它都保持在120字节丢失而不是增加,我会说你做得很好。冘。
对于桌面应用程序,小内存泄漏并不是真正的问题。对于服务(服务器),不接受内存泄漏。
这取决于您的应用程序。一些泄漏可能是不可避免的(由于找到泄漏所需的时间与截止日期)。只要您的应用程序可以随心所欲地运行,并且在此期间不占用大量内存,这可能没问题。
看起来 SDL 开发人员确实不使用 Valgrind,但我基本上只关心丢失的 120 个字节。
考虑到这一点,我一直在通过 Valgrind 运行我的"Hello world"程序以捕获任何泄漏,尽管我已经删除了除最基本的 SDL_Init() 和 SDL_Quit() 语句之外的所有内容,但 Valgrind 仍然报告丢失了 120 个字节,仍然可以访问 77k。
好吧,对于 Valgrind,"仍然可以访问的内存"通常不是真正的泄漏内存,尤其是在如此简单的程序中。我可以肯定地说,SDL_Quit() 中基本上没有分配,所以"泄漏"只是 SDL_Init() 分配一次的结构。
尝试添加有用的工作,看看这些数量是否增加;尝试循环有用的工作(如创建和销毁一些 SDL 结构),看看泄漏量是否随着迭代量的增加而增长。在后一种情况下,您应该检查泄漏的堆栈痕迹并修复它们。
否则,这些 77k 泄漏算作"应该在程序结束时释放的内存,但它们依靠操作系统来释放它。
所以,实际上,我现在更担心这 120 个字节,如果它们不是误报,而且它们通常很少。Valgrind 的误报主要是打算使用未初始化内存的情况(例如,因为它实际上是填充的)。
根据Rob Wells对Purify的评论,下载并尝试其他一些工具。 我使用BoundsChecker和AQTime,多年来在两者中都看到了不同的误报。 请注意,内存泄漏也可能在第三方组件中,您可能希望从分析中排除该组件。 例如,MFC 在第一个视图版本中有许多内存泄漏。
IMO,应追踪内存泄漏,以查找进入可能具有较长寿命的代码库的任何代码。 如果您无法追踪它们,请至少记下它们存在于同一代码的下一个用户中。
当内存泄漏随着时间的推移而增长时,它们只是一个严重的问题,否则应用程序从外面看起来会大一点(显然这里也有限制,因此"严重")。当您的泄漏随着时间的推移而增加时,您可能会遇到麻烦。不过,麻烦多少取决于情况。如果您知道内存的去向,并且可以确保您始终有足够的内存来运行该程序以及该计算机上的其他所有内容,那么您仍然有些好。但是,如果您不知道内存的去向,我不会发布该程序并继续挖掘。
特别是在Linux上的SDL中,底层的X Windows库中似乎有一些泄漏。你对这些无能为力(除非你想尝试修复库本身,这可能不适合胆小的人)。
你可以使用 valgrind 的抑制机制(参见 valgrind 手册页中的 --suppressions 和 --gen-suppressions)来告诉它不要用这些错误打扰你。
一般来说,我们必须对第三方库更加宽容;虽然我们绝对不应该接受我们自己的代码中的内存泄漏,并且在选择替代第三方库时内存泄漏的存在应该是一个因素,但有时别无选择,只能忽略它们(尽管将它们报告给库维护者可能是个好主意)。
- 错误 C2679:二进制"<<":未找到采用类型 'std::string_view' 的右侧操作数的运算符(或者没有可接受的转换)
- C2678 二进制 '==':未找到采用 'Card' 类型左操作数的运算符(或者没有可接受的转换)
- 二进制 '==':未找到采用 'Enemy' 类型左侧操作数的运算符(或者没有可接受的转换)
- C++:实现定义了可接受的物理源文件字符
- 二进制 '=':未找到采用右操作数的运算符(或者没有可接受的对话)
- 错误 C2679:二进制'<<':找不到采用类型 'overloaded-function' 的右侧操作数的运算符(或者没有可接受的转换)
- 错误:C2679 二进制"==":未找到采用类型 'const std::string' 的右侧操作数的运算符(或者没有可接受的转换)
- 是否有一种可接受的运行线程或按顺序执行的方法
- 错误 C2678:二进制"+":未找到采用类型 'volatile A' 的左侧操作数的运算符(或者没有可接受的转换)
- 错误 C2679:二进制"<<":未找到采用类型 'mystring' 的右侧操作数的运算符(或者没有可接受的转换)
- 错误 C2679:二进制'>':找不到采用类型 'int' 的右侧操作数的运算符(或者没有可接受的转换)
- 错误 C2679 二进制"=":未找到采用类型 'int' 的右侧操作数的运算符(或者没有可接受的转换)
- 错误 C2679 二进制"=":未找到采用类型 'int' 的右侧操作数的运算符(或者没有可接受的转换)
- 运算符重载:简单添加...错误 C2677:二进制"+":未找到采用类型 ___ 的全局运算符(或者不存在可接受的转换)
- C2679:二进制"<<":未找到采用类型为"student"的右操作数的运算符(或没有可接受的转换)
- 带有模板类的赋值运算符 - 没有可接受的转换,C++
- 我正在尝试检查可接受的身高和体重,并让 c++ 编译器输出候选人被拒绝的原因
- 公开对象管理器对象的可接受方法?
- 错误 C2677:二进制'+=':找不到采用类型 'Movie' 的全局运算符(或者没有可接受的转换)
- 内存泄漏是否有可接受的限制