C_C_中常见内存泄漏与对策及预防措施浅析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
对于一般小应用程序来说,一点内存 泄 漏 不 算 什 么 ;但 是 当 内 存 泄 漏 问 题 出 现 在 需 要 24小 时 运 行 的 平 台 类 程 序 上 的 时 候,将会使系统可用内存飞速减少,最后耗 尽 系 统 资 源 ,导 致 系 统 崩 溃 。为 此 探 讨 一 下 内存泄漏问题与对策及预防措施。
(4)代 码 Copy要 小 心 ,建 议 不 要 进 行 代 码 Copy操 作 。
(5)不 要 给 指 针 重 新 赋 值 。给 指 针 重 新 赋值,首先考虑设计是否合理;除建立链表 过程等特殊情况外,非空指针不要作为左 值。
(6)为 了 避 免 内 存 泄 漏 ,建 议 所 有 的 基 类加入虚析构函数。
1 内存泄漏的发生方式
以发生的方式来分类,内存泄漏可以 分为以下四类。
(1)常 发 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码会被多次执行到,每次被执行时候都 会导致一块内存泄漏。
(2)偶 发 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码只有在某些特定环境或操作过程中才 会 发 生 。常 发 性 和 偶 发 性 是 相 对 的 。对 于 特 定的环境,偶发性的也许就变成了常发性 的 。所 以 测 试 环 境 和 测 试 方 法 对 检 测 内 存 泄漏至关重要。
C/C++内 存 分 配 与 释 放 均 由 用 户 代 码 自行控制,这种机制既让程序员有更广阔 的发挥空间,也容易产生内存泄漏问题。
所谓内存泄漏通常是指堆内存的泄漏 (Memory leak)。堆 内 存 是 指 程 序 从 堆 中 分 配 的 、大 小 任 意 的 (内 存 块 的 大 小 可 以 在 程 序 运 行 期 决 定 )、使 用 完 后 必 须 显 式 释 放 的 内存;应用程序一般使用malloc、realloc、new 等函数从堆中分配到一块内存,使用完后, 程序必须负责调用相应的free或delete释放 该内存块,否则,这块内存就不能被再次使 用,于是则称这块内存泄漏了。
由 于 C/C++都 没 有 自 动 的 垃 圾 回 收 机 制 ,如 果 没 有 手 动 释 放 内 存 ,问 题 就 会 出 现 。如 果 要 避 免 这 个 问 题,还 是 要 从 代 码 上 入手,良好的编码习惯和规范,是避免错误 的 不 二 法 宝 。因 此 ,预 防 内 存 泄 漏 , 要 注 意 以下几点措施。
科技资讯 2008 NO.35
SCIENCE & TECHNOLOGY INFORMATION
} 对策“: delete p”调用了classA的析构函 数,没有调用B的析构函数,导致classB里面 申 请 的 资 源 泄 漏 。因 为 在 C++标 准 中 ,通 过 基类的指针去删除子类的对象,而基类又 没有定义虚析构函数时,结果将是不确定 的。
3 内存泄漏的预防措施
动 态 分 配 、回 收 内 存 是 C/C++编 程 语 言一个最强的特点,但是最强的同时也是 最 弱 的 。在 内 存 处 理 出 错 的 地 方 通 常 就 是 BUGS产生的地方,一个最敏感和难检测的 BUG就 是 内 存 泄 漏 ,一 个 小 的 内 存 泄 漏 可 能不需要太注意,但是程序泄漏大块内存, 或者渐增式的泄漏内存可能引起的现象 是:先是降低效率,再就是引起复杂的内存 耗 尽 错 误 。最 坏 的 是,一 个 内 存 泄 漏 程 序 可 能用完了如此多的内存以至于引起其他的 程序出错,留给用户的是不能知道错误到 底 来 自 哪 里 。另 外 ,一 个 看 上 去 无 害 的 内 存 泄漏可能是另一个问题的先兆。
关键词:C/C++ 内存泄漏 检测工具
中图分类号: T P 3 1 2
文献标识码: A
文章编号:1672-3791(2008)12(b)-0008-02
Strateges and Prevention of the Common Memory
Leakage in C/C++
Zhang Tianliang (College of Maths & Physics,Nanjing University of Information & Technology,Nanjing 210044)
Abstract:This paper summarizes the main forms of memory leakage in C/C++,their common categories and preventions,introduces some detecting devices,and then points out some measures to prevent them. Key words:C/C++;memory leakage;detecting devices
空,如果指针不空,那么指针原来指向的内 存 将 泄 漏 。例 如 :
char *p= new char[nSize]; q=p; 对 策 :当 q指 针 不 为 空 ,对 其 重 新 赋 值 后 ,q以 前 指 向 的 内 存 泄 漏 。本 例 中 可 以 再 动态分配一个空指针,使p指向它。 (5)缺少else处理分支导致的内存泄漏。 例如: char *p= new char[nSize]; iCount= SortProc(p); if(iCount<= 0) ret= 0; else if(iCount<= 5) ret= DealProc(p,iCount-1); if(ret<= 0) delete p; 对 策 :当 iCount>5时 ,ret取 值 不 确 定 , 当 ret大 于 0时 ,没 有 释 放 p,造 成 内 存 泄 漏 。 本 例 中 需 添 加 两 个 else语 句 ,用 来 判 断 iCount >5时ret的取值,以及ret大于0时,释 放p。 (6)删 除 指 针 顺 序 错 误 导 致 的 内 存 泄 漏 。例 如 : Test::~ Test() { delete(p); if(NULL!= p && NULL!= p-> pData) { free(p->pData); } } 对 策 :当 p已 经 被 删 除 了 ,那 么 if条 件 永 远不成立,于是这条free语句永远不会被执 行 ,即 p->pData占 用 的 内 存 没 有 被 释 放 。本 例中应调整删除指针顺序。 (7)析构函数忘记释放内存导致的内存 泄 漏 。例 如 : Test::~ Test() { } 对策:在析构函数里面对资源释放(非 静态成员指针或资源)或清零(静态成员指 针或资源)是一个良好的习惯,否则容易产 生内存泄漏。 (8)基类没有定义虚析构函数引起的内 存 泄 漏 。例 如 : classA { ~ A(){}//析 构 函 数 不 是 虚 函 数 } classB:publicA// classB继承了classA { ~B(){} } void main() { A *p = new B; delete p;
void MyFunction(int nSize)
{ char *p= new char[nSize];
if(!GetString(p,nSize))
{ messageBox“( Error”);
return;
} ……
delete p;
} 这是一个简单而又典型的内存泄漏示 例 ,当 函 数 GetString()调 用 错 误 时 ,函 数 MyFunction结束而指针指向的内存却没有 被 释 放,此 时 便 出 现 了 内 存 泄 漏 。在 程 序 段 入口处分配内存,在出口处释放内存,但是 函数可以在任何地方退出,所以一旦有某 个出口处没有释放应该释放的内存,就很 容易发生内存泄漏。 对 策 :C/C++函 数 可 以 在 任 何 地 方 退 出,在 每 个 出 口 处 释 放 应 该 释 放 的 内 存 。本 例中只要在return这个出口处释放内存p即 可。 (3)重 复 分 配 内 存 发 生 的 内 存 泄 漏 。例 如:
存,但是对于一个服务器程序,需要运行几 天几周甚至几个月,不及时释放内存也可 能导致最终耗尽系统的所有内存,所以称 这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄 漏本身不会产生什么危害,作为一般的用 户,根本感觉不到内存泄漏的存在,真正有 危害的是内存泄漏的积累,这会最终消耗 尽 系 统 所 有 的 内 存 。从 这 个 角 度 来 说,一次 性内存泄漏并没有什么危害,因为它不会 堆积,而隐式内存泄漏危害性则非常大,因 为较之于常发性和偶发性内存泄漏它更难 被检测到。
① 基 金 项 目 :南 京 信 息 工 程 大 学《 数 值 计 算 方 法 》精 品 课 程 建 设 项 目 (JG032006J03)。
8
科技资讯 S C I E N C E & T E C H N O L O G Y I N F O R M A T I O N
信 息 技 术
漏。 给指针赋值时,没有检查指针是否为
void MyFunction(int nSize) { ……
char *p= new char[nSize]; char *p= new char[nSize]; ……
} 对策:重复分配内存,第一块内存永远 无 法 使 用 。这 种 情 况 一 般 多 发 生 在 编 码 过 程 中 使 用“ 代 码 复 制 ”时 出 现 错 误 ,因 此 复 制代码要谨慎。 (4)非空指针被重新赋值发生的内存泄
科技资讯 2008 NO.35 SCIENCE & TECHNOLOGY INFORMATION
信 息 技 术
C / C + + 中常见内存泄漏与对策及预防措施浅析①
张天良 ( 南京信息工程大学数理学院 南京 2 1 0 0 4 4 )
摘 要: 本文综述了 C/C++ 中内存泄漏的发生方式、常见内存泄漏及其对策,简介了检测内存泄漏的工具,提出了预防内存泄漏发生的措施。
(3)一 次 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码只会被执行一次,或者由于算法上的 缺陷,导致有且仅有一块内存发生泄漏。
(4)隐 式 内 存 泄 漏 。程 序 在 运 行 过 程 中 不停的分配内存,但是直到结束的时候才 释 放 内 存 。严 格 地 说 这 里 并 没 有 发 生 内 存 泄漏,因为最终程序释放了所有申请的内
2 常见内存泄露与对策
(1)在使用局部指针变量或静态指针变 量 中 的 内 存 泄 漏 。例 如 :
int main() { int *pi; pi=new int[100]; return; } 这是常见的使用局部变量时出现的内 存 泄 漏 , 没 有 释 放 指 针 变 量 。再 如 : int main() { static int *pi=0; for(int i=0;i<10;i++)
(5)它的来自百度文库代码使用GNU许可发布并有 详 尽 的 文 档 及 注 释 。对 于 想 深 入 了 解 堆 内 存管理的读者,这是一个不错的的选择。
(1)养成及时释放内存和资源的习惯。 不再使用的内存和资源一定要及时释放, 不 要 等 到 函 数 最 后 。后 面 有 N个 返 回 ,就 需 要 N个 指 针 和 资 源 释 放 。忘 记 一 处 ,就 会 出 问题。
(2)异 常 处 理 要 全 面 ,不 要 遗 忘 :空 指 针 释放。
( 3 ) 小 心 有 r e tu r n 语 句 的 宏 。如 果 使 用 该 宏前有申请内存没有释放,将造成内存泄 漏。
pi=new int; return; } 这是常见的使用静态指针变量时出现 的 内 存 泄 漏 。程 序 退 出 后 ,分 配 的 10个int只 有 最 后 一 个 是 reachable的 ,而 前 面 9个 int则 泄漏了。 对策:释放全部指针变量。 (2)在使用动态分配内存空间中的内存
泄 漏 。例 如 :
(4)代 码 Copy要 小 心 ,建 议 不 要 进 行 代 码 Copy操 作 。
(5)不 要 给 指 针 重 新 赋 值 。给 指 针 重 新 赋值,首先考虑设计是否合理;除建立链表 过程等特殊情况外,非空指针不要作为左 值。
(6)为 了 避 免 内 存 泄 漏 ,建 议 所 有 的 基 类加入虚析构函数。
1 内存泄漏的发生方式
以发生的方式来分类,内存泄漏可以 分为以下四类。
(1)常 发 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码会被多次执行到,每次被执行时候都 会导致一块内存泄漏。
(2)偶 发 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码只有在某些特定环境或操作过程中才 会 发 生 。常 发 性 和 偶 发 性 是 相 对 的 。对 于 特 定的环境,偶发性的也许就变成了常发性 的 。所 以 测 试 环 境 和 测 试 方 法 对 检 测 内 存 泄漏至关重要。
C/C++内 存 分 配 与 释 放 均 由 用 户 代 码 自行控制,这种机制既让程序员有更广阔 的发挥空间,也容易产生内存泄漏问题。
所谓内存泄漏通常是指堆内存的泄漏 (Memory leak)。堆 内 存 是 指 程 序 从 堆 中 分 配 的 、大 小 任 意 的 (内 存 块 的 大 小 可 以 在 程 序 运 行 期 决 定 )、使 用 完 后 必 须 显 式 释 放 的 内存;应用程序一般使用malloc、realloc、new 等函数从堆中分配到一块内存,使用完后, 程序必须负责调用相应的free或delete释放 该内存块,否则,这块内存就不能被再次使 用,于是则称这块内存泄漏了。
由 于 C/C++都 没 有 自 动 的 垃 圾 回 收 机 制 ,如 果 没 有 手 动 释 放 内 存 ,问 题 就 会 出 现 。如 果 要 避 免 这 个 问 题,还 是 要 从 代 码 上 入手,良好的编码习惯和规范,是避免错误 的 不 二 法 宝 。因 此 ,预 防 内 存 泄 漏 , 要 注 意 以下几点措施。
科技资讯 2008 NO.35
SCIENCE & TECHNOLOGY INFORMATION
} 对策“: delete p”调用了classA的析构函 数,没有调用B的析构函数,导致classB里面 申 请 的 资 源 泄 漏 。因 为 在 C++标 准 中 ,通 过 基类的指针去删除子类的对象,而基类又 没有定义虚析构函数时,结果将是不确定 的。
3 内存泄漏的预防措施
动 态 分 配 、回 收 内 存 是 C/C++编 程 语 言一个最强的特点,但是最强的同时也是 最 弱 的 。在 内 存 处 理 出 错 的 地 方 通 常 就 是 BUGS产生的地方,一个最敏感和难检测的 BUG就 是 内 存 泄 漏 ,一 个 小 的 内 存 泄 漏 可 能不需要太注意,但是程序泄漏大块内存, 或者渐增式的泄漏内存可能引起的现象 是:先是降低效率,再就是引起复杂的内存 耗 尽 错 误 。最 坏 的 是,一 个 内 存 泄 漏 程 序 可 能用完了如此多的内存以至于引起其他的 程序出错,留给用户的是不能知道错误到 底 来 自 哪 里 。另 外 ,一 个 看 上 去 无 害 的 内 存 泄漏可能是另一个问题的先兆。
关键词:C/C++ 内存泄漏 检测工具
中图分类号: T P 3 1 2
文献标识码: A
文章编号:1672-3791(2008)12(b)-0008-02
Strateges and Prevention of the Common Memory
Leakage in C/C++
Zhang Tianliang (College of Maths & Physics,Nanjing University of Information & Technology,Nanjing 210044)
Abstract:This paper summarizes the main forms of memory leakage in C/C++,their common categories and preventions,introduces some detecting devices,and then points out some measures to prevent them. Key words:C/C++;memory leakage;detecting devices
空,如果指针不空,那么指针原来指向的内 存 将 泄 漏 。例 如 :
char *p= new char[nSize]; q=p; 对 策 :当 q指 针 不 为 空 ,对 其 重 新 赋 值 后 ,q以 前 指 向 的 内 存 泄 漏 。本 例 中 可 以 再 动态分配一个空指针,使p指向它。 (5)缺少else处理分支导致的内存泄漏。 例如: char *p= new char[nSize]; iCount= SortProc(p); if(iCount<= 0) ret= 0; else if(iCount<= 5) ret= DealProc(p,iCount-1); if(ret<= 0) delete p; 对 策 :当 iCount>5时 ,ret取 值 不 确 定 , 当 ret大 于 0时 ,没 有 释 放 p,造 成 内 存 泄 漏 。 本 例 中 需 添 加 两 个 else语 句 ,用 来 判 断 iCount >5时ret的取值,以及ret大于0时,释 放p。 (6)删 除 指 针 顺 序 错 误 导 致 的 内 存 泄 漏 。例 如 : Test::~ Test() { delete(p); if(NULL!= p && NULL!= p-> pData) { free(p->pData); } } 对 策 :当 p已 经 被 删 除 了 ,那 么 if条 件 永 远不成立,于是这条free语句永远不会被执 行 ,即 p->pData占 用 的 内 存 没 有 被 释 放 。本 例中应调整删除指针顺序。 (7)析构函数忘记释放内存导致的内存 泄 漏 。例 如 : Test::~ Test() { } 对策:在析构函数里面对资源释放(非 静态成员指针或资源)或清零(静态成员指 针或资源)是一个良好的习惯,否则容易产 生内存泄漏。 (8)基类没有定义虚析构函数引起的内 存 泄 漏 。例 如 : classA { ~ A(){}//析 构 函 数 不 是 虚 函 数 } classB:publicA// classB继承了classA { ~B(){} } void main() { A *p = new B; delete p;
void MyFunction(int nSize)
{ char *p= new char[nSize];
if(!GetString(p,nSize))
{ messageBox“( Error”);
return;
} ……
delete p;
} 这是一个简单而又典型的内存泄漏示 例 ,当 函 数 GetString()调 用 错 误 时 ,函 数 MyFunction结束而指针指向的内存却没有 被 释 放,此 时 便 出 现 了 内 存 泄 漏 。在 程 序 段 入口处分配内存,在出口处释放内存,但是 函数可以在任何地方退出,所以一旦有某 个出口处没有释放应该释放的内存,就很 容易发生内存泄漏。 对 策 :C/C++函 数 可 以 在 任 何 地 方 退 出,在 每 个 出 口 处 释 放 应 该 释 放 的 内 存 。本 例中只要在return这个出口处释放内存p即 可。 (3)重 复 分 配 内 存 发 生 的 内 存 泄 漏 。例 如:
存,但是对于一个服务器程序,需要运行几 天几周甚至几个月,不及时释放内存也可 能导致最终耗尽系统的所有内存,所以称 这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄 漏本身不会产生什么危害,作为一般的用 户,根本感觉不到内存泄漏的存在,真正有 危害的是内存泄漏的积累,这会最终消耗 尽 系 统 所 有 的 内 存 。从 这 个 角 度 来 说,一次 性内存泄漏并没有什么危害,因为它不会 堆积,而隐式内存泄漏危害性则非常大,因 为较之于常发性和偶发性内存泄漏它更难 被检测到。
① 基 金 项 目 :南 京 信 息 工 程 大 学《 数 值 计 算 方 法 》精 品 课 程 建 设 项 目 (JG032006J03)。
8
科技资讯 S C I E N C E & T E C H N O L O G Y I N F O R M A T I O N
信 息 技 术
漏。 给指针赋值时,没有检查指针是否为
void MyFunction(int nSize) { ……
char *p= new char[nSize]; char *p= new char[nSize]; ……
} 对策:重复分配内存,第一块内存永远 无 法 使 用 。这 种 情 况 一 般 多 发 生 在 编 码 过 程 中 使 用“ 代 码 复 制 ”时 出 现 错 误 ,因 此 复 制代码要谨慎。 (4)非空指针被重新赋值发生的内存泄
科技资讯 2008 NO.35 SCIENCE & TECHNOLOGY INFORMATION
信 息 技 术
C / C + + 中常见内存泄漏与对策及预防措施浅析①
张天良 ( 南京信息工程大学数理学院 南京 2 1 0 0 4 4 )
摘 要: 本文综述了 C/C++ 中内存泄漏的发生方式、常见内存泄漏及其对策,简介了检测内存泄漏的工具,提出了预防内存泄漏发生的措施。
(3)一 次 性 内 存 泄 漏 。发 生 内 存 泄 漏 的 代码只会被执行一次,或者由于算法上的 缺陷,导致有且仅有一块内存发生泄漏。
(4)隐 式 内 存 泄 漏 。程 序 在 运 行 过 程 中 不停的分配内存,但是直到结束的时候才 释 放 内 存 。严 格 地 说 这 里 并 没 有 发 生 内 存 泄漏,因为最终程序释放了所有申请的内
2 常见内存泄露与对策
(1)在使用局部指针变量或静态指针变 量 中 的 内 存 泄 漏 。例 如 :
int main() { int *pi; pi=new int[100]; return; } 这是常见的使用局部变量时出现的内 存 泄 漏 , 没 有 释 放 指 针 变 量 。再 如 : int main() { static int *pi=0; for(int i=0;i<10;i++)
(5)它的来自百度文库代码使用GNU许可发布并有 详 尽 的 文 档 及 注 释 。对 于 想 深 入 了 解 堆 内 存管理的读者,这是一个不错的的选择。
(1)养成及时释放内存和资源的习惯。 不再使用的内存和资源一定要及时释放, 不 要 等 到 函 数 最 后 。后 面 有 N个 返 回 ,就 需 要 N个 指 针 和 资 源 释 放 。忘 记 一 处 ,就 会 出 问题。
(2)异 常 处 理 要 全 面 ,不 要 遗 忘 :空 指 针 释放。
( 3 ) 小 心 有 r e tu r n 语 句 的 宏 。如 果 使 用 该 宏前有申请内存没有释放,将造成内存泄 漏。
pi=new int; return; } 这是常见的使用静态指针变量时出现 的 内 存 泄 漏 。程 序 退 出 后 ,分 配 的 10个int只 有 最 后 一 个 是 reachable的 ,而 前 面 9个 int则 泄漏了。 对策:释放全部指针变量。 (2)在使用动态分配内存空间中的内存
泄 漏 。例 如 :