一个偶然的机会,发现一个进程使用了超过14G的内存。这个进程是一个RPC server,只是作为中转,绝对不应该使用这么多内存的。即使并发量太多,存在内存中的数据太多,那么在并发减少的情况下,这个内存使用肯定会降下来。但是事实上,这个内存会一直涨,直到被OOM Killer杀掉。

由于这个rpc server的逻辑比较简单,先走读源码,除了发现一些简单的编程上面的问题外,没有大的问题。先上valgrind:

valgrind --tool=memcheck --leak-check=full -v ./rpc_server

原来的情况是,一般都会检查出内存泄露的。这次没有(否则也不会有本文了):

实际上至少10G的“内存泄露”。既然没有检查出,说明这些内存还是活着的。设想一下这个场景:每个请求都new一块内存,放到一个列表中。正常的话请求处理完需要从这个列表中删除这块内存。如果没有删除,那么这就算是内存泄露。但是valgrind检查不出来。

由于上面这个进程使用了tcmalloc,是不是tcmalloc的问题?我们知道tcmalloc的效率要优于malloc,那么是不是tcmalloc的问题,如果它一直申请内存,不释放,就会造成这种”内存泄露“。注意下面一段话:

Releasing Memory Back to the System

By default, tcmalloc will release no-longer-used memory back to the kernel gradually,
over time.
The tcmalloc_release_rate flag controls how quickly this happens.
You can also force a release at a given point in the progam execution like so: MallocExtension::instance()->ReleaseFreeMemory();
You can also call SetMemoryReleaseRate() to change the tcmalloc_release_rate value
 at runtime, or GetMemoryReleaseRate to see what the current release rate is.

简单翻译一下,就是tcmalloc将内存交回OS的机制:默认情况下,tcmalloc会将长时间未用的内存交还系统。tcmalloc_release_rate这个flag控制了这个交回频率。你可以在运行时通过这个语句强制这个release发生:

 MallocExtension::instance()->ReleaseFreeMemory();

当然了,你可以通过SetMemoryReleaseRate() 来设置这个tcmalloc_release_rate. 如果设置为0,代表永远不交回。数字越大代表交回的频率越大。一般合理的值就是设置一个0 - 10 之间的一个数。也可以通过设置环境变量TCMALLOC_RELEASE_RATE来设置这个rate。

带着这个怀疑,首先还是通过Google's gpreftools检查一下heap的使用情况:

1.  export HEAPCHECK=draconian

2.  export PPROF_PATH=/usr/local/bin/pprof

直接启动即可。

之所以设置为draconian,因为想得到更详细的统计信息。更将相信的解释如下:Flavors of Heap Checking

These are the legal values when running a whole-program heap check:

  1. minimal
  2. normal
  3. strict
  4. draconian

"Minimal" heap-checking starts as late as possible in a initialization, meaning you can leak some memory in your initialization routines (that run before main(), say), and not trigger a leak message. If you frequently (and purposefully) leak data in one-time global initializers, "minimal" mode is useful for you. Otherwise, you should avoid it for stricter modes.

"Normal" heap-checking tracks live objects and reports a leak for any data that is not reachable via a live object when the program exits.

"Strict" heap-checking is much like "normal" but has a few extra checks that memory isn't lost in global destructors. In particular, if you have a global variable that allocates memory during program execution, and then "forgets" about the memory in the global destructor (say, by setting the pointer to it to NULL) without freeing it, that will prompt a leak message in "strict" mode, though not in "normal" mode.

"Draconian" heap-checking is appropriate for those who like to be very precise about their memory management, and want the heap-checker to help them enforce it. In "draconian" mode, the heap-checker does not do "live object" checking at all, so it reports a leak unless all allocated memory is freed before program exit. (However, you can use IgnoreObject() to re-enable liveness-checking on an object-by-object basis.)

"Normal" mode, as the name implies, is the one used most often at Google. It's appropriate for everyday heap-checking use.

In addition, there are two other possible modes:

  • as-is
  • local

as-is is the most flexible mode; it allows you to specify the various knobs of the heap checker explicitly. local activates the explicit heap-check instrumentation, but does not turn on any whole-program leak checking.

但是很不幸,还是没有检查出来:

上面的泄露统计不是预期的,因为“泄露”了至少10G的内存了。

那么还是强制的释放不用的buffer吧:

MallocExtension::instance()->ReleaseFreeMemory();

问题解决了。

Linux Debugging (九) 一次生产环境下的“内存泄露”的更多相关文章

  1. [原]生产环境下的nginx.conf配置文件(多虚拟主机)

    [原]生产环境下的nginx.conf配置文件(多虚拟主机) 2013-12-27阅读110 评论0 我的生产环境下的nginx.conf配置文件,做了虚拟主机设置的,大家可以根据需求更改,下载即可在 ...

  2. 生产环境下JAVA进程高CPU占用故障排查

    问题描述:生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析:1,程序属于CPU密集型,和开发沟通过, ...

  3. 一次生产环境下MongoDB备份还原数据

    最近开发一个版本的功能当中用到了MongoDB分页,懒于造数据,于是就研究了下从生产环境上导出数据到本地来进行测试. 研究了一下,发现MongoDB的备份还原和MySQL语法还挺类似,下面请看详细介绍 ...

  4. iptables 生产环境下基础设置

    iptables 生产环境下基础设置 生成环境需求:防火墙需要让内网的Ip全部通过,外网IP添加到白名单,其他一切拒绝.安装在linux系统中安装yum install iptables-servic ...

  5. 生产环境下JAVA进程高CPU占用故障排查---temp

    问题描述:生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析:1,程序属于CPU密集型,和开发沟通过, ...

  6. Python开发程序:生产环境下实时统计网站访问日志信息

    日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ...

  7. 生产环境下一定要开启mysqlbinlog

    在没有备份数据库的情况下,可以用binlog进行恢复 在生产环境下安全第一,损失一点点效率换来的安全还是值得的. http://www.cnblogs.com/zc22/archive/2013/06 ...

  8. mysql在生产环境下有大量锁表,又不允许重启的情况下的处理办法

    mysql在生产环境下有大量锁表,又不允许重启的情况下的处理办法 满头大汗的宅鸟该怎么办呢? mysql -u root -e "show processlist"|grep -i ...

  9. Mysql迁移工具在生产环境下的使用

    在产品迭代开发发布过程中,由于业务需求的增加,数据库难免会有结构调整等操作. 在每个版本发布过程中怎么控制每个版本server端程序与数据库版本保持一致,以及数 据库升级.回滚等操作. 本博文宅鸟将向 ...

随机推荐

  1. [Luogu 1730]最小密度路径

    Description 给出一张有N个点M条边的加权有向无环图,接下来有Q个询问,每个询问包括2个节点X和Y,要求算出从X到Y的一条路径,使得密度最小(密度的定义为,路径上边的权值和除以边的数量). ...

  2. 洛谷P3159 [CQOI2012]交换棋子

    巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点 ...

  3. 【BZOJ3531】【SDOI2014】旅行

    题目传送门 题目大意:给定一棵无根树,每个节点有自己的类别和权值,现在给定两个类别相同的点,叫你求这2点路径上同类别节点的权值和/最大权值. 节点类别与权值会改变. 解题思路:考虑对每一个类别开一棵线 ...

  4. [APIO2012]

    来自FallDream的博客,未经允许,请勿转载,谢谢. --------------------------------------------------- A.dispatching 派遣 上次 ...

  5. C语言程序设计第四次作业-选择结构

    (一)改错题 输出三角形的面积和周长,输入三角形的三条边a.b.c,如果能构成一个三角形,输出面积area和周长perimeter(保留2位小数):否则,输出"These sides do ...

  6. Java 反射实现实体转Map时,父类元素丢失

    public class BeanToMap { public static Map<String, Object> ConvertObjToMap(Object obj) { Map&l ...

  7. Java正则过滤

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class LongStringtonumber { pu ...

  8. 特殊权限 SUID、SGID、Sticky

    摘录之----------QuintinX 一. 前提 本篇主要讲解SUID, SGID, Sticky三个权限的基本原理和应用. 为什么要使用特殊权限? 比如系统中假如有超过四类人然而每一类人都需要 ...

  9. 一起撸个简单粗暴的Tv应用主界面的网格布局控件(上)

    这一篇是真的隔了好久了~~,也终于可以喘口气来好好写博客了,这段时间实在是忙不过来了,迭代太紧.好,废话不多说,进入今天的主题. 效果 图一是Tv应用:当贝市场的主页 图二是咱自己撸的简单粗暴的 Tv ...

  10. Node.js OS 模块

    Node.js os 模块提供了一些基本的系统操作函数.我们可以通过以下方式引入该模块: var os = require("os") 方法 序号 方法 & 描述 1 os ...