文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一起聊一聊如何计算 Node.js GC 负载

2024-11-30 12:51

关注

在 Node.js 中,我们关注的比较的是 CPU 负载,但是在有 GC 的语言中,GC 负载也是需要关注的一个指标,因为 GC 过高会影响我们应用的性能。本文介绍关于 GC 负载的一些内容。

如何获取 GC 耗时

操作系统本身会计算每隔线程的 CPU 耗时,所以我们可以通过系统获取这个数据,然后计算出线程的 CPU 负载。但是 GC 不一样,因为 GC 是应用层的一个概念,操作系统是不会感知的,在 Node.js 里,具体来说,是在 V8 里,也没有 API 可以直接获取 GC 的耗时,但是 V8 提供了一些 GC 的钩子函数,我们可以借助这些钩子函数来计算出 GC 的负载。其原理和 CPU 负载类似。V8 提供了以下两个钩子函数,分别在 GC 开始和结束时会执行。

Isolate::GetCurrent()->AddGCPrologueCallback();
Isolate::GetCurrent()->AddGCEpilogueCallback();

通过这两个函数,我们就可以得到每一次 GC 的耗时,再不断累积就可以计算出 GC 的总耗时,从而计算出 GC 负载。下面看一下核心实现。

static void BeforeGCCallback(Isolate* isolate,
                             v8::GCType gc_type,
                             v8::GCCallbackFlags flags,
                             void* data) {
    GCLoad* gc_load = static_cast(data);
    if (gc_load->current_gc_type != 0) {
        return;
    }
    gc_load->current_gc_type = gc_type;
    gc_load->start_time = uv_hrtime();
}

static void AfterGCCallback(Isolate* isolate,
                            v8::GCType gc_type,
                            v8::GCCallbackFlags flags,
                            void* data) {
    GCLoad* gc_load = static_cast(data);
    if (gc_load->current_gc_type != gc_type) {
        return;
    }
    gc_load->current_gc_type = 0;
    gc_load->total_time += uv_hrtime() - gc_load->start_time;
    gc_load->start_time = 0;
}

void GCLoad::Start(const FunctionCallbackInfo& args) {
    GCLoad* obj = ObjectWrap::Unwrap(args.Holder());
    Isolate::GetCurrent()->AddGCPrologueCallback(BeforeGCCallback, static_cast(obj));
    Isolate::GetCurrent()->AddGCEpilogueCallback(AfterGCCallback, static_cast(obj));
}

可以看到思路很简单,就是注册两个 GC 钩子函数,然后在 GC 开始钩子中记录开始时间,然后在 GC 结束钩子中记录结束时间,并算出一次 GC 的耗时,再累加起来,这样就可以得到任意时刻 GC 的总耗时,但是拿到总耗时如何计算出 GC 负载呢?

如何计算 GC 负载

负载 = 过去一段时间内的消耗 / 过去的一段时间值,看看如何计算 GC 负载。

class GCLoad {
    lastTime;
    lastTotal;
    binding = null;
    start() {
        if (!this.binding) {
            this.binding = new binding.GCLoad();
            this.binding.start();
        }
    }
    stop() {
        if (this.binding) {
            this.binding.stop();
            this.binding = null;
        }
    }
    load() {
        if (this.binding) {
            const { lastTime, lastTotal } = this;
            const now = process.hrtime();
            const total = this.binding.total();
            this.lastTime = now;
            this.lastTotal = total;
            if (lastTime && lastTotal) {
                const cost = total - lastTotal;
                const interval = (now[0] - lastTime[0]) * 1e6 + (now[1] - lastTime[1]) / 1e3;
                return cost / interval;
            }
        }
    }
    total() {
        if (this.binding) {
            return this.binding.total();
        }
    }
}

计算算法也很简单,就是记录上次的时间和 GC 耗时,然后下次需要记录某个时刻的 GC 负载时,就拿当前的耗时减去上次的耗时,并拿当前的时间减去上次的时间,然后得到过去一段时间内的耗时和过去的时间大小,一处就得到 GC 负载了。

使用

下面看看如何使用。

const { GCLoad } = require('..');
const gcLoad = new GCLoad();
gcLoad.start();
setInterval(() => {
    for (let i = 0; i < 1000; i++) {
        new Array(100);
    }
    gc();
    console.log(gcLoad.load());
}, 3000);

执行上面代码会(node --expose-gc demo.js) 在我电脑上输出如下。

0.004235378248715853
0.004100483670865412
0.0017808558192331187
0.002371772559838465
0.0024768595957239477

这样就可以得到了应用的 GC 负载。

完整代码参考 https://github.com/theanarkh/nodejs-native-gc-load。

来源:编程杂技内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯