DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

目录

一、内存泄漏

一、介绍

二、几种内存泄露的场景

三、预防与排查

1、valgrind

二、内存越界

一、介绍

二、几种内存越界的情况

三、预防与排查


 

一、内存泄漏

一、介绍

  • 内存泄漏,是指在程序代码中动态申请的、堆上的内存 由于某种原因、在使用后没有被释放,进而造成内存的浪费。
  • 少部分的内存泄漏不会影响程序的正常运行,不过如果是持续的内存泄漏会耗光系统内存,最终会导致程序卡死甚至系统崩溃。为了避免系统崩溃,在无法申请到内存的时候,要果断调用exit()函数主动杀死进程,而不是试图挽救这个进程。

二、几种内存泄露的场景

1、malloc/new申请的内存没有主动释放

 
 
void test1()
 
{
 
char* str = new char[100];
 
/*delete[] str; 这里忘记delete了 */
 
}
 
 

2、new与free混用,malloc与delete混用

 
 
class Base
 
{
 
public:
 
int* values;
 
Base() { values = new int[100]; }
 
~Base() { delete[] values; }
 
};
 
 
 
void test2()
 
{
 
Base* pBase = new Base;
 
 
 
free(pBase);
 
/* 错误,这样只会释放pBase指向的内存,却不会调用Base的析构函数
 
会导致Base中的values指向的内存无法被释放 */
 
 
 
delete pBase; /* 正确 */
 
}
 
 

3、使用new开辟数组时,delete忘记加[]

 
 
void test3()
 
{
 
int* vects = new int[100];
 
 
 
delete vects; // 这种写法是错误的
 
delete[] vects; // 这种写法是正确的
 
}
 
 

4、基类的析构函数没有定义为虚函数

 
 
class A
 
{
 
public:
 
A() {}
 
~A() {}
 
};
 
 
 
class B : public A
 
{
 
public:
 
B() { num = new int[100]; }
 
~B() { delete[] num; }
 
private:
 
int* num;
 
};
 
 
 
void main() {
 
A* pa = new B();
 
 
 
delete pa;
 
/* 这样只会调用A类的析构函数,而B类中的num就得不到释放
 
正确的是应该把A的析构类声明为虚函数 */
 
}
 
 

三、预防与排查

1、valgrind

  • 应用环境:Linux
  • 编程语言:C/C++
  • 使用方法:  编译时加上-g选项,如 gcc -g filename.c  -o filename,使用如下命令检测内存使用情况:
  • 结果输出:#valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./filename,就会看到内存使用报告
  • 设计思路:根据软件的内存操作维护一个有效地址空间表和无效地址空间表(进程的地址空间)
  • 优缺点:能够检测:
    • 使用未初始化的内存 (Use of uninitialised memory)
    • 使用已经释放了的内存 (Reading/writing memory after it has been free’d)
    • 使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
    • 对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
    • 申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
    • malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
    • src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
    • 重复free
  • 如何获取:http://valgrind.org/

2、使用智能指针

 
 
void test4()
 
{
 
// 以下两种写法效果相同
 
shared_ptr<int> p1 = shared_ptr<int>(new int(234));
 
shared_ptr<int> p2 = make_shared<int>(234);
 
 
 
// 还有其他指针指针
 
weak_ptr<int> p3 = weak_ptr<int>(p1);
 
unique_ptr<int> p4 = unique_ptr<int>(new int(234));
 
auto_ptr<int> p5 = auto_ptr<int>(new int(234));
 
}
 
 

二、内存越界

一、介绍

  • 内存越界是软件系统主要错误之一,其后果往往不可预料且非常严重。更麻烦的是,它出现的时机是随机的,表现出来的症状是随机的,而且造成的后果也是随机的,这会使程序员很难找出这些 Bug 的现象和本质之间的联系,从而给 Bug 的定位带来极大的困难。一般情况下,内存越界访问可分如下两种:
  • 读越界,即读了不属于自己的数据。如果所读的内存地址是无效的,程序立刻崩溃;如果所读内存地址是有效的,在读的时候不会马上出现问题,但由于读到的数据是随机的,因此它会造成不可预料的后果。
  • 写越界,又称为缓冲区溢出,所写入的数据对别的程序来说是随机的,它也会造成不可预料的后果。

二、几种内存越界的情况

  1. 定义指针的时候未初始化,所以指针指向的时一块随机值,用户并不一定有访问权限。
  2. 分配到的内存比实际上使用的内存要小。
  3. 使用下标访问数组时,下标错误。
  4. 内存已经被释放了,但仍指针来使用这块内存。

三、预防与排查

  1. 定义指针变量的时候,如果暂时不初始化,可以先让它指向 nullptr。
  2. 分配内存时,用户要检查内存是否分配成功。
  3. 使用下标访问数组的时候,要检查是否越界。
  4. 内存被释放之后,指向这块内存的指针也要赋值为 nullptr。
 
posted on 2023-04-19 14:12  DoubleLi  阅读(2094)  评论(0编辑  收藏  举报