nodejs学习: 服务器资源CPU、内存、硬盘

对于一个后端开发来说,要时刻关注服务器资源CPU、内存、硬盘。

那怎么获取到资源的信息呢?

其实通过 node 的原生 api 就可以做到。

CPU

js 复制代码
os.cpus()

我们创建一个nest服务:

js 复制代码
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import * as os from 'os';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
  @Get('status')
  status() {
    return os.cpus();
  }
}

返回的数组元素个数就是 cpu 数。

那具体的属性是什么意思呢?

times.user、times.sys、times.idle 分别代表用户代码占用的 cpu 时间、系统代码占用的 cpu 时间,空闲的 cpu 时间。

基于这些就能算出 cpu 的使用率、空置率来。

js 复制代码
@Get('status')
status() {
    const cpus = os.cpus();
    const cpuInfo = cpus.reduce(
      (info, cpu) => {
        info.cpuNum += 1;
        info.user += cpu.times.user;
        info.sys += cpu.times.sys;
        info.idle += cpu.times.idle;
        info.total += cpu.times.user + cpu.times.sys + cpu.times.idle;
        return info;
      },
      { user: 0, sys: 0, idle: 0, total: 0, cpuNum: 0 },
    );
    const cpu = {
      cpuNum: cpuInfo.cpuNum,
      sys: ((cpuInfo.sys / cpuInfo.total) * 100).toFixed(2),
      used: ((cpuInfo.user / cpuInfo.total) * 100).toFixed(2),
      free: ((cpuInfo.idle / cpuInfo.total) * 100).toFixed(2),
    };
    return cpu;
}

用 reduce 方法累加 cpu 的数量、user、sys、idle 以及总的 cpu 时间。

然后 cpu 的系统使用率就是 sys/total,用户使用率是 user/total 而空置率就是 idle/total。

内存

memoryUsage 核心作用

process.memoryUsage() 是 Node.js 内置的 process 模块提供的 API,用于实时获取当前 Node.js 进程的内存使用详情,返回一个包含多个关键指标的对象,是排查内存泄漏、评估服务内存占用的核心工具。

它不需要安装任何依赖,直接调用即可。

js 复制代码
const memoryInfo = process.memoryUsage();
console.log('内存使用详情:', memoryInfo);

// 输出示例(单位:字节)
// {
//   rss: 45000000,        // 常驻集大小
//   heapTotal: 18000000,  // 堆总内存
//   heapUsed: 9000000,    // 堆已使用内存
//   external: 1200000,    // 外部内存
//   arrayBuffers: 100000  // ArrayBuffer 占用内存
// }

所有字段默认单位是 字节 (Bytes) ,可转换为 MB(除以 1024*1024)更易读,各字段含义如下:

字段名 中文释义 核心说明
rss 常驻集大小 (Resident Set Size) Node.js 进程占用的物理内存总大小(包括堆、栈、外部库、代码段等),是操作系统视角的内存占用
heapTotal V8 堆总内存 V8 引擎为 JavaScript 对象分配的总堆内存空间(已申请但未必全部使用)
heapUsed V8 堆已使用内存 V8 引擎实际用于存储 JavaScript 对象(变量、函数、数组等)的内存,排查内存泄漏的核心指标
external 外部内存 V8 管理的、不在 V8 堆中的内存(如 Buffer 数据、C++ 扩展占用的内存)
arrayBuffers ArrayBuffer 内存 所有 ArrayBufferSharedArrayBuffer 占用的内存(属于 external 的子集)

heapTotal 代表 V8 引擎为 JavaScript 对象分配的总堆内存空间 (已向操作系统申请但未必全部使用)。V8 采用动态扩容 机制,当满足以下条件时,heapTotal 会自动增大:

  1. 堆内存不足 :当前 heapUsed(已使用堆内存)接近 heapTotal 时,V8 会判断「现有堆空间不够用」,主动向操作系统申请更多内存,导致 heapTotal 上升;
  2. 垃圾回收(GC)后仍不足 :V8 会先触发垃圾回收,尝试释放无用内存。如果 GC 后 heapUsed 依然接近 heapTotal 上限,就会触发堆扩容;
  3. 大对象分配:程序中创建大数组、大对象(比如一次性加载大量数据)时,V8 会直接扩容堆空间以容纳这些对象。

Node.js 默认限制 V8 堆内存(64 位系统约 1.4GB,32 位 0.7GB),可通过启动参数调整,适用于大内存场景:

js 复制代码
# 将 V8 老年代堆内存上限调整为 4GB 
node --max-old-space-size=4096 your-app.js

系统内存

上面是nodejs的进程的内存,使用os可以获取服务器的内存情况。

js 复制代码
bytesToGB(bytes) {
    const gb = bytes / (1024 * 1024 * 1024);
    return gb.toFixed(2);
}

getMemInfo() {
    const totalMemory = os.totalmem();
    const freeMemory = os.freemem();
    const usedMemory = totalMemory - freeMemory;
    const memoryUsagePercentage = (((totalMemory - freeMemory) / totalMemory) * 100).toFixed(2);
    const mem = {
      total: this.bytesToGB(totalMemory),
      used: this.bytesToGB(usedMemory),
      free: this.bytesToGB(freeMemory),
      usage: memoryUsagePercentage,
    };
    return mem;
}

// 结果GB
"mem": {
    "total": "24.00",
    "used": "23.73",
    "free": "0.27",
    "usage": "98.88"
 },

可对比进程内存与系统总内存,评估资源占用比例:

js 复制代码
const processMem = (process.memoryUsage().rss / os.totalmem() * 100).toFixed(2);

内存泄露

内存泄漏指的是:Node.js 进程中已分配的内存,在不再被使用的情况下,无法被 V8 垃圾回收(GC)机制释放,导致 heapUsed 持续上涨,最终耗尽 V8 堆内存上限,触发 JavaScript heap out of memory 错误

V8 对堆内存有默认上限(可通过启动参数调整):

  • 64 位系统:默认约 1.4 GB(老年代堆);
  • 32 位系统:默认约 0.7 GB;
  • 如果 heapTotal 达到上限后,heapUsed 仍持续增长,会抛出 JavaScript heap out of memory 错误(内存溢出)。

以下是最常见的内存泄露的场景:

1. 未清理的全局变量

Node.js 中未声明的变量会自动挂载到 global 对象上,成为全局引用,永远不会被 GC 回收。

js 复制代码
// 错误示例:未声明变量,自动挂载到 global
function loadData() {
  // 没有 let/const/var,data 成为全局变量
  data = Array(1000000).fill({ name: 'leak' }); 
}

// 调用后,data 一直存在于全局,无法回收
loadData();

2. 未移除的事件监听器

EventEmitter 的事件监听如果重复绑定、且未在使用后移除,会保留回调函数的引用,导致关联对象无法回收。

js 复制代码
const EventEmitter = require('events');
const emitter = new EventEmitter();

// 错误示例:每次调用都新增监听器,旧监听器无法回收
function addListener() {
  // 匿名回调引用了外部大对象
  const bigData = Array(1000000).fill('leak');
  emitter.on('data', () => {
    console.log(bigData);
  });
}

// 多次调用,监听器堆积,内存暴涨
for (let i = 0; i < 10; i++) {
  addListener();
}

3. 缓存未设置过期 / 清理策略

手动实现的缓存(比如对象 / Map)如果只加不减,会无限膨胀,导致内存泄漏。

js 复制代码
// 错误示例:缓存只存不取,无限增长
const cache = new Map();

function setCache(key) {
  const data = Array(1000000).fill('leak');
  cache.set(key, data);
  // 没有清理逻辑,缓存永远不会被回收
}

// 持续添加缓存,内存耗尽
let key = 0;
setInterval(() => {
  setCache(key++);
}, 100);

修复:设置缓存过期时间,或限制缓存大小:

js 复制代码
const cache = new Map();
const MAX_CACHE_SIZE = 100; // 限制缓存最大数量

function setCache(key) {
  const data = Array(1000000).fill('leak');
  
  // 超过上限时清理最早的缓存
  if (cache.size >= MAX_CACHE_SIZE) {
    const firstKey = cache.keys().next().value;
    cache.delete(firstKey);
  }
  
  cache.set(key, data);
}

还有很多导致内存泄露的原因,比如未正确销毁的数据库连接、未结束的流(Stream)等等

内存泄漏的核心特征是 heapUsed 持续上涨且不回落」,可通过以下方式监控识别:

js 复制代码
// 每5秒输出 heapUsed,观察趋势
setInterval(() => {
  const { heapUsed, heapTotal } = process.memoryUsage();
  console.log(`heapUsed: ${(heapUsed / 1024 / 1024).toFixed(2)} MB, heapTotal: ${(heapTotal / 1024 / 1024).toFixed(2)} MB`);
}, 5000);

判断标准

  • 正常情况:heapUsed 涨涨跌跌(GC 会定期释放);
  • 泄漏情况:heapUsed 持续上升,即使触发 GC 也只小幅回落,最终接近 heapTotal 上限。

硬盘

这里用到 node-disk-info 这个包:

js 复制代码
npm install --save node-disk-info

getDiskStatus() {
    const disks = nodeDiskInfo.getDiskInfoSync();

    const sysFiles = disks.map((disk) => {
      return {
        mounted: disk.mounted,
        filesystem: disk.filesystem,
        total: this.bytesToGB(disk.blocks) + 'GB',
        used: this.bytesToGB(disk.used) + 'GB',
        free: this.bytesToGB(disk.available) + 'GB',
        usage: ((disk.used / disk.blocks || 0) * 100).toFixed(2),
      };
    });
    return sysFiles;
  }

分别是路径、文件系统、总大小、已用大小、可用大小、已用百分比:

系统其他信息

js 复制代码
@Get('status')
async status() {
    return {
      cpu: this.getCpuInfo(),
      mem: this.getMemInfo(),
      dist: await this.getDiskStatus(),
      sys: this.getSysInfo()
    }
}

getSysInfo() {
    return {
      computerName: os.hostname(),
      computerIp: this.getServerIP(),
      osName: os.platform(),
      osArch: os.arch(),
    };
}

getServerIP() {
    const nets = os.networkInterfaces();
    for (const name of Object.keys(nets)) {
      for (const net of nets[name]) {
        if (net.family === 'IPv4' && !net.internal) {
          return net.address;
        }
      }
    }
}

这里的 os.networkInterfaces 是拿到所有网卡信息:

从中过滤出非 IPv4 的外部网卡的 ip 来返回。

和我系统设置里的 ip 一样:

此外,我们还通过 os.homename、os.platform、os.arch 分别拿到了主机名、操作系统、操作系统架构等信息。

相关推荐
Mr_li3 小时前
手摸手,教你如何优雅的书写 NestJS 服务配置
node.js·nestjs
QQ51100828519 小时前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
q***098019 小时前
最新最详细的配置Node.js环境教程
node.js
WeiXin_DZbishe19 小时前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5
遥遥江上月19 小时前
Node.js + Stagehand + Python 部署
开发语言·python·node.js
YAY_tyy19 小时前
2025 最新版 Node.js 下载安装及环境配置教程
前端·node.js·教程·工具配置
爱的叹息19 小时前
Node.js 所有主要版本的发布时间、稳定版本(Stable)和长期支持版本(LTS) 的整理
node.js
一勺菠萝丶19 小时前
MacBook 上如何正确安装 nvm 和 Node.js(新手必看)
node.js
仰望尾迹云19 小时前
Chandra AI与Node.js集成:实时聊天应用开发全攻略
node.js·大语言模型·ai聊天·实时对话