Linux中主要是C,C是Linux的“母语”,这也是linux这个开源环境和本身机制所导致的,就连linus都力挺C,而驳斥C++。虽然没必要拒绝C++,但是,不可否认,C更适合linux~。
Linux操作系统主要包括内核和组件系统。Linux内核大部分是用C语言编写的,还有部分是用汇编语言写的,因为在对于硬件上,汇编有更好的性能和速度。
Linux的一些组件系统和附加应用程序是用C、C++、Python、perl等语言写的。
C/C++运行高效,不管是操作系统内核还是对性有要求的程序(比如游戏引擎)都要求使用C/C++来编写,其实C/C++强大的一点在于能够使用指针自由地控制内存的使用,适时的申请内存和释放内存,从而做到其他编程语言做不到的高效地运行。但是内存管理是一把双刃剑,用好了削铁如泥,用不好自断一臂。在申请堆上内存使用完之后中如果做不到适时有效的释放,那么就会造成内存泄露,久而久之程序就会将系统内存耗尽,导致系统运行出问题。
C语言中申请内存和释放内存的方法是使用 malloc函数(动态内存分配)和free(释放)。
C++中能兼容C,所以也能使用malloc和free,面向对象的情况下使用的则是new和delete,能够自动执行构造函数和析构函数。
首先知道为什么要排查内存,排查内存泄漏的主要原因许多程序在内存使用完成之后如果做不到适时有效的释放,那么就会造成内存泄漏(也就是孤儿进程较多),久而久之程序就会将系统内存耗尽,导致运行出现卡顿、或崩溃的现象发生。
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个lnteger(是数据类型之一),但给它存了long才能存下的数,那就是内存溢出。
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.
内存泄漏可以分为四类:
1.常发性内存泄漏。主要就是每次执行额时候都会导致一块内存泄漏。
2.偶发性内存泄漏。
3.一次性内存泄漏。
4.隐式内存泄漏。按照正常来说程序在运行过程中不停的分配内存,结束的时候才释放内存。在运行过程中不会发生内存泄漏,但一个服务器程序,运行的时间较长后会不释放内存导致最终耗尽系统的所有内存。(这类的内存泄漏危害性较大,因为较之于常发性和偶尔性内存泄漏它更难被检测到)
排查工具:valgrind(工具)(valgrind是一个提供了一些debug和优化的工具的工具箱。可以使你的程序减少内存泄漏或者错误的访问,valgrind默认使用memcheck(诊断内存)去检查内存问题)
下载安装包地址:(https://valgrind.org/downloads/current.html#current)(无网络时可用U盘)
解压:tar xvf valgrind-3.14.0.tar.bz2
手工编译安装:
或者在Linux中用yum仓库进行安装(命令为:sudo yum install -y valgrind)(连接网络时)自己本人试了一下yum仓库,但没找到valgrind文件位置,最好还是手工编译安装吧:
安装及解压tar xvf valgrind-3.18.1.tar.bz2
cd valgrind-3.18.1
./configure
make
sudo make install
输入 valgrind --help 查看是否安装成功
(可以查到版本,但是查不到文件的位置,脑阔疼。有空再研究一下)
memcheck能够检测出内存问题,关键在于其建立了两个全局表。
(1)valid-value map(有效值地图):主要监控地址空间、cpu中每个字节或寄存器值是否是有效的、初始化的值。
(2)Valid-address map(有效地址地图):主要监控地址空间中的每个字节和对应的bit,负责记录能否被正常读取。
检测的原理:(1)读取内存中字节时,首先检查valid-address map中这个字节对应的A bit。如果该A bit显示位置无效,memcheck会报错。
(3)内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit (在 valid-value map 中) 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的 V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。
Valgrind的使用方法:首先编译好要测试的程序(因为valgrind是非常精准的工具,可以定位到源代码行,在编译时加上-g参数)
valgrind命令的格式如下: valgrind [valgrind-options] your-prog [your-prog options]
-h --help显示帮助信息
–version 显示valgrind内核版本
-q --quiet 安静运行,只打印错误信息
-v --verbose 打印更详细的信息
–tool=[default:memcheck] 最常用的选项。运行valgrind中为toolname的工具。
**接下来需要用C程序脚本运行起来,然后用valgrind工具查看内存占用
案例:
void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; // problem 1: heap block overrun(堆块溢出)
} // problem 2: memory leak -- x not freed(内存泄漏,X未被释放)
int main(void)
{
f();
return 0;
}
这个test是目标文件,不是可执行文件,因为这里用到了-c,告诉gcc到汇编为止,不要进行链接。
这个test.o和test一样,都是目标文件
会为每个源文件生成一个.o文件,但此时是不能使用-o的。
运行脚本命令为bash,但不能直接运行,必须要解决应用环境问题。简单理解就是要建立文件路径再建立软链接。
解决方法:出现此报错的原因是因为,此时的test.c还不是可编译文件,我们要先使用gcc-c,-o指令,如下图:
执行test文件,命令为:./ test (因为我直接在root下建立的文件所以直接运行即可)
运行后可用工具valgrind检测test程序,命令为:valgrind --leak-check=yes ./test,如下图所示:
非静态内部类创建静态实例造成的内存泄漏
将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例(是我们项目中经常使用的一个设计模式),如果需要使用Context,就使用Application的Context(小巧、快速、功能强大的文本编辑工具)。或者把当前类的上下文,改成application.
(内存泄漏方向的种类偏多,是个漫长的学习过程,有很多不理解的地方,我会在接下来学习过程中慢慢完善,然后解决更新文档,最主要的是C语言太多,不会是关键。)哈哈哈哈哈!
(其实我们在工作中,岗位是运维工程师,主要职能是保障运行在服务器中的程序能正常运行,在这个过程中我们能查到问题并第一时间解决问题是关键,如查到问题自己无法解决,且在目前为止不影响服务程序的正常运行情况下。我们可以协调开发部门去解决,毕竟服务器中运行的程序开发语言不同如 Python, JavaScript, C#, C, PHP, Java, C++ 或者 go,这些你不可能全会也不可能都能解决,可以在协调解决的过程中去偷师学习他们如何去解决问题的,俗话说技多不压身)
当然以上测试方法必须要求运用到开发语言去写才能检测出来,下面给大家介绍一下基础命令检测内存泄漏的方法:
free或者cat /proc/meminfo:主要用于确定设备是否存在内存泄漏
Proc:/proc是一个位于内存中的伪文件系统,该目录下保存的不是真正的文件和目录,而是一些“运行时”信息,如系统内存、磁盘io、设备挂载信息和硬件配置信息等。proc目录是一个控制中心,用户可以通过更改其中某些文件来改变内核的运行状态。proc目录也是内核提供给我们的查询中心,我们可以通过这些文件查看有关系统硬件及当前正在运行进程的信息。在Linux系统中,许多工具的数据来源正是proc目录中的内容。/proc/meminfo是了解Linux系统内存使用状况的主要接口。
我们可以通过在设备启动运行一段时间后,分别用free命令查看自己使用内存used的大小并进行对比,如运行一段时间后used有很明显的增大那很可能就存在内存泄漏。
top:是实时动态地查看系统的整体运行情况,可以运行起来看占用资源最大的程序或进程
cat/proc/$pid/status (其中pid是进程号,可以通过ps命令查看应用程序的pid,然后通过命令查看该进程的内存使用情况
这里主要看VmRSS,它代表着当前正在使用的物理内存的大小,一般该进程存在内存泄漏的话,在启动时到运行一段时间后,该值会明显急剧增大。
1.通过free命令确定设备是否存在泄漏
2.通过ps命令获取要查找的应用进程的pid
3.通过cat /proc/$pid/status 中的VmRSS查看当前进程的物理内存是否相比较于刚启动时,明显增大。