Linux 一则案例了解内存泄漏

对普通进程来说,能看到的其实是内核提供的虚拟内存,这些虚拟内存还需要通过页表,由系统映射为物理内存。

当进程通过 malloc0申请虚拟内存后,系统并不会立即为其分配物理内存,而是在首次访问时, 才通过缺页异常陷入内核中分配内存。

为了协调 CPU与磁盘间的性能差异,Linux 还会使用Cache和Buffer,分别把文件和磁盘读写的数据缓存到内存中。

对应用程序来说,动态内存的分配和回收,是既核心又复杂的一个逻辑功能模块。管理内存的过程中,也很容易发生各种各样的"事故",比如

●  没正确回收分配后的内存,导致了泄漏。

●  访问的是已分配内存边界外的地址,导致程序异常退出,等等。

今天带你来看看,内存泄漏到底是怎么发生的,以及发生内存泄漏之后该如何排查和定位。说起内存泄漏,这就要先从内存的分配和回收说起了。内存的分配和回收

内存的分配与回收


先回顾一下,你还记得应用程序中,都有哪些方法来分配内存吗?用完后,又该怎么释放还给系统呢?

前面讲进程的内存空间时,我曾经提到过,用户空间内存包括多个不同的内存段,比如只读段、数据段、堆、栈以及文件映射段等。这些内存段正是应用程序使用内存的基本方式。

举个例子,你在程序中定义了一个局部变量,比如一个整数数组 int data[64],就定义了一个可以存储64个整数的内存段。由于这是一个局部变量,它会从内存空间的栈中分配内存。

栈内存由系统自动分配和管理。一旦程序运行超出了这个局部变量的作用域,栈内存就会被系统自动回收,所以不会产生内存泄漏的问题。

再比如,很多时候,我们事先并不知道数据大小,所以你就要用到标准库函数malloc0,_在程序中动态分配内存。这时候,系统就会从内存空间的堆中分配内存。

堆内存由应用程序自己来分配和管理。除非程序退出,这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数 free来释放它们。如果应用程序没有正确释放堆内存,就会造成内存泄漏。

这是两个栈和堆的例子,那么,其他内存段是否也会导致内存泄漏呢?经过我们前面的学习,这个问题并不难回答。

● 只读段,包括程序的代码和常量,由于是只读的,不会再去分配新的内存,所以也不会产生内存泄漏。

● 数据段,包括全局变量和静态变量,这些变量在定义时就已经确定了大小,所以也不会产生内存泄漏。

● 最后一个内存映射段,包括动态链接库和共享内存,其中共享内存由程序动态分配和管理。所以,如果程序在分配后忘了回收,就会导致跟堆内存类似的泄漏问题。

内存泄漏的危害非常大,这些忘记释放的内存,不仅应用程序自己不能访问,系统也不能把它们再次分配给其他应用。内存泄漏不断累积,甚至会耗尽系统内存。

虽然,系统最终可以通过OOM(Out of Memory)机制杀死进程,但进程在OOM前,可能已经引发了一连串的反应,导致严重的性能问题。比如,其他需要内存的进程,可能无法分配新的内存,内存不足,又会触发系统的缓存回收以及SWAP机制,从而进一步导致 I/O的性能问题等等。

比如,其他需要内存的进程,可能无法分配新的内存;内存不足,又会触发系统的缓存回收以及SWAP机制,从而进一步导致 I/O的性能问题等等。

内存泄漏的危害这么大,那我们应该怎么检测这种问题呢?特别是,如果你已经发现了内存泄漏,该如何定位和处理呢。

接下来,我们就用一个计算斐波那契数列的案例,来看看内存泄漏问题的定位和处理方法。斐波那契数列是一个这样的数列∶0、1、1、2、3、5、8也就是除了前两个数是0和1,其他数都由前面两数相加得到,用数学公式来表示就是F(n)=F(n-1)+F(n-2),(n>=2),F(O)=0, F(1)=1。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/108286148