用Ruby跟踪进程的内存使用情况

2022年11月8日 - - - - - - 5分钟读
用Ruby跟踪进程的内存使用情况

应用程序使用的内存数量是调查内存膨胀的基础。为了想出这个数字每一个给定的任务是一个挑战自己当你想调试生产应用程序。如果你的可观测性服务给你号码,你去比赛;如果不是,你是一个新的旅程。在BetterUp,我们巩固了一些可观测性工具到一个供应商,Datadog。这时,Datadog没有一流的支持Ruby应用程序中的内存分析。缺少内存分析Ruby是一个回归对我们来说,考虑到我们目前的工具让我们洞察内存使用。

作为第一步,我们可以手动开始跟踪内存使用和报告任何可观测性服务。有几种不同的工具(宝石),可以帮助Ruby代码。这些工具,然而,有一些限制,通常用于一次性使用或根据需要,和不推荐用于不间断生产,因为他们听到。

问题是,我们如何规避这个开销?我们可以构建一个简单的工具,可能并不完美,但是提供了有价值的见解内存分配?和有你好的老朋友,C。我们可以利用Ruby C API和有一个复杂的基于Ruby的内部对象生命周期解决方案事件和使用rb_tracepoint_new()注册一个新的侦听器。这一切听起来非常复杂,正是我们应该做的而不是可观察性服务。

BetterUp,我们的一个高影响力的行为是“少做,提供更多”。考虑到这种行为,我们挑战自己想出一个更简单的解决方案。可以认为,一个好的开始是看进程的内存使用情况,这将为我们提供一个很好的指标我们的代码的内存使用;这就是我们将做的。建筑部分之前,让我们介绍内存膨胀和注意在一个进程的内存使用情况。

内存膨胀

内存膨胀时出现内存分配大幅增加在一个应用程序。因此,应用程序使用的内存数量在整个生命周期变得异常,影响其性能。也就是说,收集信息在内存使用是至关重要的。我们不需要一个完美的解决方案。相反,我们需要一个指标,可能需要进一步的调查。为什么不开始通过查看进程的内存使用量?

进程的内存使用情况

在一个多线程的世界,获取进程的内存使用量不告诉我们很多内存使用以来的全貌可能来自一个不同的线程比被分析。然而,它提供了一个很好的起点看的地方在我们的应用程序对内存分配模式可能出现的地方。的红宝石3.1.2,没有好办法偷看一个进程的内存使用情况。一个是留给使用分析工具根据需要生产,从Ruby代码运行Unix命令,或者从Ruby读取进程的文件系统。为什么不利用底层接口,这一过程的所有信息吗?

你的第一个Ruby C扩展

哦,等等,C代码?现在再见。

我们可以使用C库收集流程级的信息。就是这样一个图书馆sys / resource.h在C语言中,这给了我们getrusage ()。这就是我们将做的事情:

  • 创建一个宝石
  • 添加一个本地扩展这个宝石
  • 得到ru_maxrss getrusage ()(医生从这个本地扩展)

首先,我们创建一个新的宝石。我们可以用打包机支架的结构这一新的宝石。

包宝石getmaxrss

这个命令将创建一个目录与基础结构为我们的宝石。

让我们集中在lib文件夹。现在,我们应该有一个这样的文件夹结构:

Rakefile lib / getmaxrss。rb……

本地扩展的文件应该生活在一个文件夹在ext目录下。这个文件夹的名字应该是我们扩展的名称相同。它应该是这样的:

Rakefile lib / getmaxrss。rb ext / getmaxrss /……

现在,我们有ext / getmaxrss /目录,我们需要包含两个文件。首先,我们创建一个extconf.rb该目录下的文件。这个文件将会讲述一个配置Makefile如何构建我们的扩展。最后,第二个文件是我们的C扩展源。让我们创建一个文件命名getmaxrss.c,熊名称相同的扩展。到目前为止,我们有:

Rakefile lib / getmaxrss。rb ext / getmaxrss / extconf。rb ext / getmaxrss / getmaxrss。c…

让我们配置extconf.rb。在这一步中,您可能需要检查任何依赖您的扩展。在我们的例子中,我们将使用它来检查如果目标系统sys / resource.h头和getrusage功能。

我们的extconf.rb应该是这样的:

需要“mkmf”中止(“失踪的< sys /资源。h >头在这个系统上!”),除非have_header (sys / resource.h)中止(“失踪getrusage()这个系统上!”),除非have_func (getrusage) create_makefile (“getmaxrss / getmaxrss”)

上面的代码使用mkmf附带Ruby库,建立一个Makefile。一旦生成,我们将使用Makefile编译我们的本地扩展。

接下来,让我们写C代码扩展,进入getmaxrss.c

# include < ruby。h > # include < sys /资源。ru_maxrss h >值;/ / https://man7.org/linux/man-pages/man2/getrusage.2.html静态值get_maxrss (int _argc * _argv价值,价值_self) {struct rusage process_rusage_struct;int反应;响应= getrusage (RUSAGE_SELF &process_rusage_struct);如果(响应= = 1){rb_sys_fail(“失败执行getrusage !”);}ru_maxrss = LONG2NUM (process_rusage_struct.ru_maxrss);返回ru_maxrss;cGetmaxrss}无效Init_getmaxrss (void){值;cGetmaxrss = rb_const_get (rb_cObject rb_intern (Getmaxrss ")); rb_define_module_function(cGetmaxrss, "call", get_maxrss, -1); }

哦,等一下,发生了什么?以下是部分:

  • Init_getmaxrss——这是我们扩展的初始化钩。它需要具有相同的名称作为我们的扩展Init_ <名称>所以要求可以加载它。
  • 价值——这是一个C类型定义引用到Ruby对象的指针。
  • rb_intern——返回ID相应的对象。
  • rb_const_get-访问一个类的常量/模块。在我们的例子中,我们访问Getmaxrss模块从Ruby对象(rb_cObject)。
  • rb_define_module_function——定义一个模块函数在模块/ C需要类指针引用,方法的名字,C函数,定义了方法,和一些描述接收参数的数量。

这是一个很好的参考这个API。

让我们来看看我们get_maxrss函数。

  • 抓住当前进程通过getrusage使用信息,RUSAGE_SELF是指当前进程。
  • 只检索到ru_maxrssrusage结构。
  • 并返回ru_maxrss作为一个价值

好的,现在我们有了所有的代码,我们如何编译和使用它?

我们将使用rake-compiler宝石的发展,这将帮助我们构建扩展我们的extconf.rb文件中发展。让我们添加以下行来Rakefile:

需要“耙/ extensiontask”rake:: ExtensionTask.new (getmaxrss) | ext | ext.lib_dir = lib / getmaxrss结束

通过设置lib_dir,我们确保我们的扩展将建在来源lib / getmaxrss目录中。现在,我们可以运行rake编译,编译我们的本地扩展。

我们仍然需要需要扩展编译,我们可以通过改变lib / getmaxrss.rb:

# frozen_string_literal:真正require_relative getmaxrss /版本的模块getmaxrss结束需要‘getmaxrss / getmaxrss’

注意最后一行要求我们的C扩展。

大作。现在我们可以检查我们的宝石进入Ruby控制台bin /控制台

> / bin /控制台irb > Getmaxrss。电话= > 63455232

不要忘记添加测试,它将不会在这篇文章中介绍。

寻找模式,而不是精度

现在我们已经Getmaxrss,我们可以用它来寻找应用程序任务之间的内存分配增加web请求和后台处理等工作。这种方法是有限的,因为我们前面提到的,多线程应用程序中,并没有提供一个非常准确的照片应用程序之间的内存分配的任务,但它提供了一个起点,寻找异常模式,可能表明需要进一步调查。


关于作者

维克多是一个完整的堆栈工程师BetterUp充满热情,对软件质量,学习新技术,构建可伸缩的产品和系统。这一切都开始当他第一次在2012年加入公司,现在他积累了十多年的行业经验。

太棒了!接下来,完全访问BetterUp成套检测产品的博客。
欢迎回来!你已经成功地登录。
您已经成功订阅BetterUp产品博客。
成功!你的帐户是完全激活,你现在可以访问所有内容。
成功!您的账单信息已经更新。
你的账单没有更新。
Baidu
map