软件诊断学-存泄漏与内存管理问题

2023-11-04  本文已影响0人  若隐爱读书

内存泄漏是指程序中已动态分配的堆内存由于某种原因未释放或者无法释放,造成的内存浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏是C/C++语言中最常见的内存管理问题,因为C/C++语言需要开发者手动申请和释放内存,如果内存使用不当,就容易造成内存泄漏。本节将介绍内存泄漏的现象,定位思路,常用工具,案例分析和避免方法。

4.1.1 内存泄漏的现象

内存泄漏的主要表现是程序占用的内存持续增长,而不会随着程序的运行而释放。这会导致程序的性能下降,响应变慢,甚至出现内存不足的错误。内存泄漏的现象可以通过以下几种方式观察:

4.1.2 内存泄漏的定位思路

内存泄漏的定位思路是找出程序中哪些地方分配了内存,但是没有释放,或者释放不正确。这需要对程序的内存管理逻辑有清晰的了解,以及对内存检测工具的熟练使用。一般来说,可以按照以下步骤进行内存泄漏的定位:

4.1.3 内存泄漏的常用工具

内存泄漏的常用工具有以下几种:

4.1.4 内存泄漏的案例分析

下面给出一个内存泄漏的案例,以及如何使用Valgrind工具进行定位和解决的过程。假设有以下的C++程序,它的功能是创建一个链表,然后删除链表中的奇数节点,最后打印链表的内容:

#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
    Node(int d): data(d), next(nullptr) {}
};

Node* createList(int n) {
    Node* head = new Node(1);
    Node* cur = head;
    for (int i = 2; i <= n; i++) {
        cur->next = new Node(i);
        cur = cur->next;
    }
    return head;
}

void deleteOdd(Node* head) {
    Node* cur = head;
    Node* prev = nullptr;
    while (cur) {
        if (cur->data % 2 == 1) {
            if (prev) {
                prev->next = cur->next;
            }
            delete cur;
        } else {
            prev = cur;
        }
        cur = cur->next;
    }
}

void printList(Node* head) {
    Node* cur = head;
    while (cur) {
        cout << cur->data << &quot; &quot;;
        cur = cur->next;
    }
    cout << endl;
}

int main() {
    Node* head = createList(10);

该程序的预期输出是:

2 4 6 8 10

然而,该程序存在内存泄漏的问题,因为在删除奇数节点时,没有更新头节点的指针,导致头节点被释放后,无法访问后续的节点,造成内存的浪费。为了发现和解决这个问题,我们使用Valgrind工具对程序进行内存泄漏的检测,具体步骤如下:

g++ -o leak leak.cpp
valgrind --leak-check=full ./leak
==1234== Memcheck, a memory error detector
==1234== HEAP SUMMARY:
==1234==     in use at exit: 40 bytes in 4 blocks
==1234==   total heap usage: 11 allocs, 7 frees, 88 bytes allocated
==1234==
==1234== 40 bytes in 4 blocks are definitely lost in loss record 1 of 1
==1234==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1234==    by 0x4EBF9DF: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==1234==    by 0x108A9E: createList(int) (leak.cpp:11)
==1234==    by 0x108B5C: main (leak.cpp:38)
==1234==
==1234== LEAK SUMMARY:
==1234==    definitely lost: 40 bytes in 4 blocks
==1234==    indirectly lost: 0 bytes in 0 blocks
==1234==      possibly lost: 0 bytes in 0 blocks
==1234==    still reachable: 0 bytes in 0 blocks
==1234==         suppressed: 0 bytes in 0 blocks
==1234==
==1234== For counts of detected and suppressed errors, rerun with: -v
==1234== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
void deleteOdd(Node* head) {
    Node* cur = head;
    Node* prev = nullptr;
    while (cur) {
        if (cur->data % 2 == 1) {
            if (prev) {
                prev->next = cur->next;
            } else {
                //如果删除的是头节点,需要更新头节点的指针
                head = cur->next;
            }
            delete cur;
        } else {
            prev = cur;
        }
        cur = cur->next;
    }
}
==1234== Memcheck, a memory error detector
==1234== HEAP SUMMARY:
==1234==     in use at exit: 0 bytes in 0 blocks
==1234==   total heap usage: 11 allocs, 11 frees, 88 bytes allocated
==1234==
==1234== All heap blocks were freed -- no leaks are possible
==1234==
==1234== For counts of detected and suppressed errors, rerun with: -v
==1234== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

4.1.5 内存泄漏的避免方法

内存泄漏的避免方法有以下几种:

上一篇 下一篇

猜你喜欢

热点阅读