Linux性能优化实战
倪朋飞
微软资深工程师,Kubernetes项目维护者
立即订阅
23395 人已学习
课程目录
已完结 64 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (2讲)
开篇词 | 别再让Linux性能问题成为你的绊脚石
免费
01 | 如何学习Linux性能优化?
CPU 性能篇 (13讲)
02 | 基础篇:到底应该怎么理解“平均负载”?
03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)
04 | 基础篇:经常说的 CPU 上下文切换是什么意思?(下)
05 | 基础篇:某个应用的CPU使用率居然达到100%,我该怎么办?
06 | 案例篇:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?
07 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(上)
08 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(下)
09 | 基础篇:怎么理解Linux软中断?
10 | 案例篇:系统的软中断CPU使用率升高,我该怎么办?
11 | 套路篇:如何迅速分析出系统CPU的瓶颈在哪里?
12 | 套路篇:CPU 性能优化的几个思路
13 | 答疑(一):无法模拟出 RES 中断的问题,怎么办?
14 | 答疑(二):如何用perf工具分析Java程序?
内存性能篇 (8讲)
15 | 基础篇:Linux内存是怎么工作的?
16 | 基础篇:怎么理解内存中的Buffer和Cache?
17 | 案例篇:如何利用系统缓存优化程序的运行效率?
18 | 案例篇:内存泄漏了,我该如何定位和处理?
19 | 案例篇:为什么系统的Swap变高了(上)
20 | 案例篇:为什么系统的Swap变高了?(下)
21 | 套路篇:如何“快准狠”找到系统内存的问题?
22 | 答疑(三):文件系统与磁盘的区别是什么?
I/O 性能篇 (10讲)
23 | 基础篇:Linux 文件系统是怎么工作的?
24 | 基础篇:Linux 磁盘I/O是怎么工作的(上)
25 | 基础篇:Linux 磁盘I/O是怎么工作的(下)
26 | 案例篇:如何找出狂打日志的“内鬼”?
27 | 案例篇:为什么我的磁盘I/O延迟很高?
28 | 案例篇:一个SQL查询要15秒,这是怎么回事?
29 | 案例篇:Redis响应严重延迟,如何解决?
30 | 套路篇:如何迅速分析出系统I/O的瓶颈在哪里?
31 | 套路篇:磁盘 I/O 性能优化的几个思路
32 | 答疑(四):阻塞、非阻塞 I/O 与同步、异步 I/O 的区别和联系
网络性能篇 (13讲)
33 | 关于 Linux 网络,你必须知道这些(上)
34 | 关于 Linux 网络,你必须知道这些(下)
35 | 基础篇:C10K 和 C1000K 回顾
36 | 套路篇:怎么评估系统的网络性能?
37 | 案例篇:DNS 解析时快时慢,我该怎么办?
38 | 案例篇:怎么使用 tcpdump 和 Wireshark 分析网络流量?
39 | 案例篇:怎么缓解 DDoS 攻击带来的性能下降问题?
40 | 案例篇:网络请求延迟变大了,我该怎么办?
41 | 案例篇:如何优化 NAT 性能?(上)
42 | 案例篇:如何优化 NAT 性能?(下)
43 | 套路篇:网络性能优化的几个思路(上)
44 | 套路篇:网络性能优化的几个思路(下)
45 | 答疑(五):网络收发过程中,缓冲区位置在哪里?
综合实战篇 (13讲)
46 | 案例篇:为什么应用容器化后,启动慢了很多?
47 | 案例篇:服务器总是时不时丢包,我该怎么办?(上)
48 | 案例篇:服务器总是时不时丢包,我该怎么办?(下)
49 | 案例篇:内核线程 CPU 利用率太高,我该怎么办?
50 | 案例篇:动态追踪怎么用?(上)
51 | 案例篇:动态追踪怎么用?(下)
52 | 案例篇:服务吞吐量下降很厉害,怎么分析?
53 | 套路篇:系统监控的综合思路
54 | 套路篇:应用监控的一般思路
55 | 套路篇:分析性能问题的一般步骤
56 | 套路篇:优化性能问题的一般方法
57 | 套路篇:Linux 性能工具速查
58 | 答疑(六):容器冷启动如何性能分析?
加餐篇 (4讲)
加餐(一) | 书单推荐:性能优化和Linux 系统原理
加餐(二) | 书单推荐:网络原理和 Linux 内核实现
用户故事 | “半路出家 ”,也要顺利拿下性能优化!
用户故事 | 运维和开发工程师们怎么说?
结束语 (1讲)
结束语 | 愿你攻克性能难关
Linux性能优化实战
登录|注册

18 | 案例篇:内存泄漏了,我该如何定位和处理?

倪朋飞 2018-12-31
你好,我是倪朋飞。
通过前几节对内存基础的学习,我相信你对 Linux 内存的工作原理,已经有了初步了解。
对普通进程来说,能看到的其实是内核提供的虚拟内存,这些虚拟内存还需要通过页表,由系统映射为物理内存。
当进程通过 malloc() 申请虚拟内存后,系统并不会立即为其分配物理内存,而是在首次访问时,才通过缺页异常陷入内核中分配内存。
为了协调 CPU 与磁盘间的性能差异,Linux 还会使用 Cache 和 Buffer ,分别把文件和磁盘读写的数据缓存到内存中。
对应用程序来说,动态内存的分配和回收,是既核心又复杂的一个逻辑功能模块。管理内存的过程中,也很容易发生各种各样的“事故”,比如,
没正确回收分配后的内存,导致了泄漏。
访问的是已分配内存边界外的地址,导致程序异常退出,等等。
今天我就带你来看看,内存泄漏到底是怎么发生的,以及发生内存泄漏之后该如何排查和定位。
说起内存泄漏,这就要先从内存的分配和回收说起了。

内存的分配和回收

先回顾一下,你还记得应用程序中,都有哪些方法来分配内存吗?用完后,又该怎么释放还给系统呢?
前面讲进程的内存空间时,我曾经提到过,用户空间内存包括多个不同的内存段,比如只读段、数据段、堆、栈以及文件映射段等。这些内存段正是应用程序使用内存的基本方式。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Linux性能优化实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(78)

  • Scott
    我比较关心老版本的Linux怎么做同样的事,毕竟没有办法升级公司服务器的内核。

    作者回复: 另一个用的比较多的是valgrind

    2019-01-01
    28
  • mj4ever
    老师:
    遇到了个问题,google也查不出所以然:
    1、ubuntu 18.04,内核4.15.0-29-generic
    2、运行 memleak -a -p $(pidof app),报错:
    Attaching to pid 14069, Ctrl+C to quit.
    perf_event_open(/sys/kernel/debug/tracing/events/uprobes/p__lib_x86_64_linux_gnu_libc_2_27_so_0x97070_14069_bcc_14199/id): Input/output error
    Traceback (most recent call last):
      File "/usr/share/bcc/tools/memleak", line 416, in <module>
        attach_probes("malloc")
      File "/usr/share/bcc/tools/memleak", line 406, in attach_probes
        pid=pid)
      File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 952, in attach_uprobe
        raise Exception("Failed to attach BPF to uprobe")
    Exception: Failed to attach BPF to uprobe
    2019-01-01
    1
    11
  • 萧董
    memleak输出中一直有addr就是内存没有释放吗
    2018-12-31
    6
  • 阿卡牛
    老师,你这个例子是已经知道哪个进程有内存泄露了,请问如何找出哪个进程呢?

    作者回复: 去掉进程号选项

    2019-01-04
    5
  • 郭江伟
    本例中将动态分配内存改为使用数组,然后就不需要自己free了;
    将app.c拷贝为app2.c 做如下修改,因为篇幅有限没法贴完全代码:
    long long fibonacci(long long *n0, long long *n1)
    {
    //分配1024个长整数空间方便观测内存的变化情况
    // long long *v = (long long *) calloc(1024, sizeof(long long));
             long long v[1024];
    然后执行memleak
    gjw@gjw:~$ sudo /usr/share/bcc/tools/memleak -p $(pidof app2c)
    Attaching to pid 3463, Ctrl+C to quit.
    [13:02:24] Top 10 stacks with outstanding allocations:
    [13:02:29] Top 10 stacks with outstanding allocations:
    ^Cgjw@gjw:~$ sudo /usr/share/bcc/tools/memleak -p $(pidof app2c)
    Attaching to pid 3463, Ctrl+C to quit.
    [13:02:43] Top 10 stacks with outstanding allocations:
    [13:02:48] Top 10 stacks with outstanding allocations:
    [13:02:53] Top 10 stacks with outstanding allocations:
    [13:02:58] Top 10 stacks with outstanding allocations:
    2018-12-31
    1
    5
  • 我来也
    [D18打卡]
    想不到又有神器可以直接分析出是哪个函数导致了内存泄露。
    以前都是在申请和释放的地方加标记,然后用工具去分析。
    思考题:
    一般能预分配的空间都没必要去动态申请。
    这个案例可以把存放结果的值先定义好,函数参数中用指针过去,这样就没必要申请内存了。
    2018-12-31
    4
  • 付盼星
    老师好,我有个问题想请教下,这里的堆栈和java虚拟机的堆栈是对应起来的么?
    2018-12-31
    4
  • Maxwell
    如果是java应用程序,也可以用这个方法定位么?

    作者回复: Java 看到的是JVM 的堆栈。其实,jmap这些Java原生的工具更好用

    2019-01-02
    2
  • Aaron Cheung
    坚持初衷,死磕就行,不退缩,不放弃!
    2018-12-31
    2
  • espzest
    程序长期运行后,VSZ一直增长,其中很绝大多数是匿名页导致的,有没有没法确定这些匿名页是依然在用? 如果没有再用,能有办法强制释放他们?
    2018-12-31
    2
  • 唯安格
    老师,我运行:$ /usr/share/bcc/tools/memleak -a -p $(pidof app) 并没有看到内存泄漏的问题。之后还看了app的源码。源码内的确没有调用free()函数。请问这可能是什么情况?
    root@ubuntu:/# /usr/share/bcc/tools/memleak -p $(pidof app) -a
    Attaching to pid 84307, Ctrl+C to quit.
    [02:42:22] Top 10 stacks with outstanding allocations:
    [02:42:27] Top 10 stacks with outstanding allocations:
    [02:42:32] Top 10 stacks with outstanding allocations:
    [02:42:37] Top 10 stacks with outstanding allocations:
    [02:42:43] Top 10 stacks with outstanding allocations:
    [02:42:48] Top 10 stacks with outstanding allocations:
    [02:42:53] Top 10 stacks with outstanding allocations:
    [02:42:58] Top 10 stacks with outstanding allocations:
    [02:43:03] Top 10 stacks with outstanding allocations:
    ^Croot@ubuntu:/# docker exec app cat /app.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>

    long long *fibonacci(long long *n0, long long *n1)
    {
    long long *v = (long long *) calloc(1024, sizeof(long long));
    *v = *n0 + *n1;
    return v;
    }

    void *child(void *arg)
    {
    long long n0 = 0;
    long long n1 = 1;
    long long *v = NULL;
    for (int n = 2; n > 0; n++) {
    v = fibonacci(&n0, &n1);
    n0 = n1;
    n1 = *v;
    printf("%dth => %lld\n", n, *v);
    sleep(1);
    }
    }


    int main(void)
    {
    pthread_t tid;
    pthread_create(&tid, NULL, child, NULL);
    pthread_join(tid, NULL);
    printf("main thread exit\n");
    return 0;

    作者回复: 去掉进程号选项-p试试?

    2019-03-12
    1
  • Vicky🐣🐣🐣
    1. 如果执行/usr/share/bcc/tools/memleak -a -p [pid] 就会报错Exception: Failed to attach BPF to uprobe
    但是执行/usr/share/bcc/tools/memleak -a,就不会报错,但是里面并没有和app相关函数
    2. free观察情况如下,新机器,并没有任何其他高占用内存的进程,很是奇怪
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r b swpd free buff cache si so bi bo in cs us sy id wa st
     1 0 0 218244 112044 463144 0 0 21 31 148 344 1 0 99 0 0
     0 0 0 218268 112044 463144 0 0 0 0 168 402 0 1 99 0 0
     0 0 0 217928 112044 463144 0 0 0 7 182 427 1 0 99 0 0
     0 0 0 217836 112044 463228 0 0 23 0 317 730 1 1 98 0 0
     0 0 0 217836 112048 463236 0 0 0 17 186 437 1 0 99 0 0
     0 0 0 215804 112052 463232 0 0 0 19 202 476 1 1 98 0 0
     0 0 0 215860 112052 463240 0 0 0 5 221 490 1 1 99 0 0
     0 0 0 217040 112056 463244 0 0 0 15 207 481 1 0 99 0 0
     0 0 0 217040 112056 463244 0 0 0 0 156 363 0 0 100 0 0
     0 0 0 76976 112056 463296 0 0 24 12 221 546 11 3 86 0 0
     0 0 0 77008 112060 463316 0 0 0 11 178 407 1 1 98 0 0
     0 0 0 75140 112060 463324 0 0 0 27 176 812 2 3 95 0 0
     0 0 0 74584 112060 463328 0 0 0 7 174 819 1 1 98 0 0
     0 0 0 74616 112060 463332 0 0 0 0 183 417 0 0 99 0 0
     0 0 0 216884 112060 463332 0 0 0 83 176 403 1 0 98 1 0
     0 0 0 216884 112064 463328 0 0 0 9 180 448 0 1 99 0 0
     0 0 0 217012 112064 463336 0 0 0 4 193 452 0 1 99 0 0

    作者回复: 看一下内核配置开启CONFIG_UPROBE_EVENTS了吗?

    2019-02-23
    1
  • Vicky🐣🐣🐣
    老师,很多同学都问这个问题了,麻烦解答一下吧
    ubuntu 4.15.0-29
    # /usr/share/bcc/tools/memleak -a -p 21642
    Attaching to pid 21642, Ctrl+C to quit.
    perf_event_open(/sys/kernel/debug/tracing/events/uprobes/p__lib_x86_64_linux_gnu_libc_2_27_so_0x97070_21642_bcc_21882/id): Input/output error
    Traceback (most recent call last):
      File "/usr/share/bcc/tools/memleak", line 416, in <module>
        attach_probes("malloc")
      File "/usr/share/bcc/tools/memleak", line 406, in attach_probes
        pid=pid)
      File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 989, in attach_uprobe
        raise Exception("Failed to attach BPF to uprobe")
    Exception: Failed to attach BPF to uprobe

    作者回复: 内核中需要开启 CONFIG_UPROBE_EVENTS=y

    2019-02-23
    1
  • 元天夫
    还有一个很low的问题,Linux version 2.6.32-504.23.4.el6.x86_64 (mockbuild@c6b9.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-11),这个是我查看的内核信息,这个显示内核版本是4.4.7对吗?

    作者回复: 4.4.7是Red Hat产品的版本,内核版本是2.6.32

    2019-02-22
    1
    1
  • 仲鬼
    老师好,之前哪节课讲过pmap?并没有找到

    作者回复: 没有专门讲它的使用方法,不过很容易查到它的使用手册

    2019-01-04
    1
  • 夜空中最亮的星(华仔)
    老师,代码段里面可否把 代码前面的 $ 或 # 号,去掉。带着还的手动去掉下才能执行代码

    作者回复: 还是需要留着,去掉就不容易区分注释、命令和输出了

    2019-01-02
    1
  • 划时代
    memleak好像要比valgrind进行内存泄漏检测要方便很多。

    作者回复: 是的

    2019-01-02
    1
  • code2
    防止内存泄露,在c中最好让malloc和free成对出现,不要在函数中分配,在函数外释放,这样一不留神就忘了,检查时也不容易发现。也可使用一些源代码内存泄露检测工具。在C++中除了成对出现外还要注意new和delete使用的一些要点。曾遇到过一个投资数千万的大项目,java做的,因内存泄露不能查明原因,服务器不得不每月杀掉服务进程,重新启动。
    2019-01-01
    1
  • 黄智寿
    请问老师,对于golang或者python之类的进程有没有相应的工具分析内存泄露
    2019-01-01
    1
  • David.cui
    老师讲的都是进程和操作系统之间的内存问题,我想请教一下老师如果是进程里面有多线程,如果怀疑发生了内存泄漏,有什么办法可以处理或分析
    2018-12-31
    1
收起评论
78
返回
顶部