文档章节

监视Rails进程内存泄漏的技巧

Yashin
 Yashin
发布于 2015/05/06 13:50
字数 1298
阅读 34
收藏 0
点赞 0
评论 0
Rails应用比较容易遇到的两类性能问题:一类是Rails执行很慢,CPU消耗过高;另一类是Rails进程内存泄漏。解决这两类问题都需要你首先能够精确定位出现问题的代码,然后才知道如何对症下药。

一、如何监控Rails进程的执行性能

定位消耗CPU高,执行速度缓慢的Rails代码,是相当容易的事情,仅仅需要你对production.log做一点统计分析,抽取出来执行时间最长的请求,问题就昭然若揭了。由于production.log对Rails请求的执行时间做了详细的统计,例如:

Completed in 0.00693 (144 reqs/sec) | Rendering: 0.00489 (70%) | DB: 0.00000 (0%) | 200 OK [http://www.iteye.com/]
Completed in 0.17238 (5 reqs/sec) | Rendering: 0.10011 (58%) | DB: 0.06244 (36%) | 200 OK [http://www.iteye.com/topic/49441?page=7]
Completed in 0.20508 (4 reqs/sec) | Rendering: 0.19373 (94%) | DB: 0.00645 (3%) | 200 OK [http://www.iteye.com/news/1586]


所以我们只需要写一行shell命令,就搞定了!他把最耗时的前500个请求筛选出来,保存到timing.log里面。

grep "200 OK" production.log | awk '{print "ALL: " $3 "  View: " $8 " DB: " $12 "  URL: " $17 }' \
| sort -r | head -n 500 > timing.log


排序好的结果例如:

ALL: 5.51774  View: 5.38277 DB: 0.13338  URL: [http://www.iteye.com/wiki/topic/131966]
ALL: 5.51316  View: 5.31300 DB: 0.19400  URL: [http://www.iteye.com/wiki/topic/145383]
ALL: 5.51311  View: 5.39321 DB: 0.11234  URL: [http://www.iteye.com/wiki/topic/160370]
ALL: 5.51135  View: 5.37604 DB: 0.12652  URL: [http://www.iteye.com/wiki/topic/233365]
ALL: 5.49881  View: 5.35998 DB: 0.10637  URL: [http://www.iteye.com/wiki/topic/265217]


哪些请求执行的慢,一目了然。 当然除此之外,我们还可以实时监控, 在top监视窗口显示Rails当前正在执行的请求URL

二、如何监控Rails进程的内存泄漏

监控CPU是很容易的事情,但要监控Rails进程的内存泄漏,却非常困难,原因在于production.log里面并没有记录进程的内存变化状况,甚至你找不到任何ruby API可以用来直接查询到进程使用的物理内存。实际上,要获取一个进程的物理内存是一个平台相关的操作,每个操作系统都会自己特定的API,并不通用,即使用C语言来编码,也不是一件容易的事情。

不过对于Linux操作系统来说,我们有一个捷径可以获取进程的内存状况。Linux的/proc文件系统是内核的映象,/proc/进程pid/status 文件记录了这个进程的状态信息,例如:
Name:   dispatch.fcgi
State:  S (sleeping)
SleepAVG:       135%
Tgid:   26645
Pid:    26645
PPid:   1
TracerPid:      0
Uid:    1002    1002    1002    1002
Gid:    100     100     100     100
FDSize: 64
Groups: 14 16 17 33 100 
VmSize:   245680 kB
VmLck:         0 kB
VmRSS:    209104 kB
VmData:   205116 kB
VmStk:       824 kB
VmExe:       764 kB
VmLib:      4220 kB
Threads:        1
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000002006e47
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000


注意第14行VmRSS,记录了该进程使用的常驻物理内存(Residence),这个就是该进程实际占用的物理内存了。因此只要我们读取该文件第14行,就可以得到内存信息。

所以我们的任务变成了:在Rails处理请求之前记录内存,等Rails处理完请求之后,再记录内存,计算内存的变化状况,写入到production.log里面去。完成这个工作,只需要我们在Rails应用的app/controllers/application.rb里面添加几行代码:

around_filter :record_memory
  def record_memory
    process_status = File.open("/proc/#{Process.pid}/status")
    13.times { process_status.gets }
    rss_before_action = process_status.gets.split[1].to_i
    process_status.close
    yield
    process_status = File.open("/proc/#{Process.pid}/status")
    13.times { process_status.gets }
    rss_after_action = process_status.gets.split[1].to_i
    process_status.close
    logger.info("CONSUME MEMORY: #{rss_after_action - rss_before_action} \
KB\tNow: #{rss_after_action} KB\t#{request.url}")
  end


我们定义了一个AroundFilter,记录一下处理请求前后的内存变化。有了这个信息,我们接下来的事情就简单了,只需要从production.log里面抽取出来这行log,进行统计分析就可以了,这也仅仅只需要一行shell就搞定了:

grep "CONSUME MEMORY" production.log | grep -v "CONSUME MEMORY: 0" |  \
 grep -v "CONSUME MEMORY: -" |  awk '{print $3 "\t" $6 "\t" $8 }' | sort -r -n | \
 head -n 500 > memory.log


抽取内存记录,去掉内存没有增加,去掉内存减少(发生了GC)的请求,然后对那些处理请求之后内存上升的记录进行排序,取出来前500条记录保存到memory.log里面,结果如下所示:

增加数 内存占用    请求URL
-----------------------------------------------
9528  175264  http://www.iteye.com/topic/304594
9524  129512  http://knityster.iteye.com/blog/172990
9496  147544  http://www.iteye.com/forums/
9492  197800  http://duyiwuer.iteye.com/rss
9452  146668  http://www.iteye.com/forums
9452  133844  http://wildlife.iteye.com/blog/47693
9440  157824  http://www.iteye.com/rss/blogs
9424  204664  http://www.iteye.com/wiki/topic/251964
9384  142200  http://towerhe.iteye.com/blog/93704
9380  165372  http://www.iteye.com/wiki/topic/77434
9368  207460  http://superleo.iteye.com/rss


第一列是访问了一个请求以后,Rails进程的内存上升了9MB多,第二列是处理完请求,Rails进程当前实际占了170多MB内存,第三列是处理了什么请求。

根据这个统计结果,你可以很容易找出那些造成你Rails进程内存泄漏的罪魁祸首,哪些请求一访问你的Rails进程内存就飚升已经是一目了然的事情了,这是不是很简单?事实上通过这个办法,JavaEye仅用了半个多小时,就解决了曾经困扰了半年多的内存泄漏问题,办法虽土,却很有效!

本文转载自:http://robbin.iteye.com/blog/307271

共有 人打赏支持
Yashin

Yashin

粉丝 252
博文 55
码字总数 5378
作品 1
深圳
高级程序员
Perfmon.exe辅助检查.NET程序内存泄漏

因为工作用C#写的程序老是内存泄漏,在网上找了找资料后,发现了Windows自带的性能监视器Perfmon.exe可以辅助查看.NET程序的运行状况。今天研究了一番,下面的内容就是一些我认为比较重要需要...

北风其凉
2014/07/30
0
0
Linux服务器的系统内存监控方法详细解析

一、内存使用情况监测 (1)实时监控内存使用情况 在命令行使用“Free”命令可以监控内存使用情况 Mem: 256024 192284 63740 0 10676 101004 -/+ buffers/cache: 80604 175420 Swap: 522072...

红薯
2009/05/07
603
0
关于 Apache mpm(多路处理模块)

MPM 有多个,常用的有:prefork、worker、event、winnt,其中前3个用于类unix系统,后者为Windows系统设计使用,其他如:netware、mpmt_os2 等暂时笔者还没用过。 查看当前使用的MPM模块:a...

首席吊炸天
2012/10/23
0
0
在 Linux 平台中调试 C/C++ 内存泄漏方法

由于 C 和 C++ 程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误。同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的 后果。从历史上看,...

Dicky
2011/09/25
0
0
HybridDB · 源码分析 · MemoryContext 内存管理和内存异常分析

背景 最近排查和解决了几处 HybridDB for PostgreSQL 内存泄漏的BUG。觉得有一定通用性。 这期分享给大家一些实现细节和小技巧。 阿里云上的 HybridDB for PostgreSQL 是基于 PostgreSQL 开发...

阿里云RDS-数据库内核组
2017/07/08
0
0
Linux 内存泄漏检测

一、New / Delete原理 当我们在程序中写下 new 和 delete 时,我们实际上调用的是 C++ 语言内置的 new operator 和 delete operator。所谓语言内置就是说我们不能更改其含义,它的功能总是一...

吃一堑消化不良
2016/10/21
18
0
Ruby 社区应该去 Rails 化了

从Linkedin和Iron.io抛弃ruby说起 最近半年关于Ruby编程语言最负面的两条新闻莫过于2012年10月的报导:Linkedin从ruby迁移到node.js,30台服务器减到3台,以及2013年3月的报导:Iron.io从rub...

oschina
2013/03/27
6K
28
JDK的可视化工具系列 (四) JConsole、VisualVM

JConsole: Java监视与管理控制台 代码清单1: import java.util.*; public class JConsoleDemo { } 内存监控: 编译运行JConsoleDemo类, 运行时设置的虚拟机参数为 -Xms100m -Xmx100m -XX:+Use...

qingshanli
昨天
0
0
每天实践一点点

以最小的代价换取最大的收益 运维实用技巧之Htop详解 1. htop简介 Htop是一款运行于Linux系统监控与进程管理软件,用于取代Unix下传统的top。与top只提供最消耗资源的进程列表不同,htop提供...

god142
2014/04/27
0
0
Android内存泄漏分析实战

内存泄漏简介 java可以保证当没有引用指向对象的时候,对象会被垃圾回收器回收,与c语言自己申请的内存自己释放相比,java程序员轻松了很多,但是并不代表java程序员不用担心内存泄漏。当jav...

拉偶有所依
2015/06/26
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring Cloud Gateway 接口文档聚合实现

在微服务架构下,通常每个微服务都会使用Swagger来管理我们的接口文档,当微服务越来越多,接口查找管理无形中要浪费我们不少时间,毕竟懒是程序员的美德。 由于swagger2暂时不支持webflux 走...

冷冷gg
19分钟前
10
0
流利阅读笔记30-20180719待学习

1.今日导读 2.带着问题听讲解 3.新闻正文(中英文对照) 4.重点词汇 5.拓展内容

aibinxiao
21分钟前
1
0
OSChina 周五乱弹 —— 我们是食物链的最底层

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @温家成 :分享谢安琪的单曲《姿色份子》 《姿色份子》- 谢安琪 手机党少年们想听歌,请使劲儿戳(这里) @贪吃飒:最近p2p怎么了、半个月爆了...

小小编辑
35分钟前
6
1
Android Studio 3.0 之后打包apk出现应用未安装问题

1、废话 出现这个问题的原因,并不是只有一个,而是有多个原因,不懂的估计会被搞得一头雾水,下面我列举的是我遇到的几种问题和网友遇到的几种问题,但不一定是全部,也有可能有些莫名其妙的...

她叫我小渝
54分钟前
0
0
前端基础

1. get请求传参长度的误区 误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。 实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是...

wenxingjun
今天
0
0
拦截SQLSERVER的SSL加密通道替换传输过程中的用户名密码实现运维审计(一)

工作准备 •一台SQLSERVER 2005/SQLSERVER 2008服务 •SQLSERVER jdbc驱动程序 •Java开发环境eclipse + jdk1.8 •java反编译工具JD-Core 反编译JDBC分析SQLSERVER客户端与服务器通信原理 SQ...

紅顏為君笑
今天
9
0
jQuery零基础入门——(六)修改DOM结构

《jQuery零基础入门》系列博文是在廖雪峰老师的博文基础上,可能补充了个人的理解和日常遇到的点,用我的理解表述出来,主干出处来自廖雪峰老师的技术分享。 在《零基础入门JavaScript》的时...

JandenMa
今天
0
0
linux mint 1.9 qq 安装

转: https://www.jianshu.com/p/cdc3d03c144d 1. 下载 qq 轻聊版,可在百度搜索后下载 QQ7.9Light.exe 2. 去wine的官网(https://wiki.winehq.org/Ubuntu) 安装 wine . 提醒网页可以切换成中...

Canaan_
今天
0
0
PHP后台运行命令并管理运行程序

php后台运行命令并管理后台运行程序 class ProcessModel{ private $pid; private $command; private $resultToFile = ''; public function __construct($cl=false){......

colin_86
今天
1
0
数据结构与算法4

在此程序中,HighArray类中的find()方法用数据项的值作为参数传递,它的返回值决定是否找到此数据项。 insert()方法向数组下一个空位置放置一个新的数据项。一个名为nElems的字段跟踪记录着...

沉迷于编程的小菜菜
今天
1
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部