使用Node.js開發後端程式的時候,在某些情況下可能會需要查看運行環境還剩下多少的記憶體來決定是否繼續進行工作,那麼該如何使用Node.js來獲取運行環境的記憶體資訊呢?



os.totalmem和os.freemem

Node.js內建的「os」模組其實就有提供「totalmem」和「freemem」函數來分別抓取作業系統的記憶體總量和閒置的記憶體,用法如下:

const os = require('os');

console.log(os.freemem()); // 8488792064 (bytes)
console.log(os.totalmem()); // 33638309888 (bytes)

然而在實際應用上,只知道記憶體總量和閒置的記憶體是沒什麼用處的。因為現在的作業系統都傾向利用更多沒有使用到的記憶體來做快取和緩衝,來加速非主記憶體裝置的I/O速度,當作業系統需要分配超過閒置記憶體大小的記憶體給行程使用時,不足的部份就會藉由釋放被用來做快取和緩衝的記憶體區塊來補足。所以透過「os.freemem()」所得到的閒置記憶體總是比作業系統實際的「可用記憶體」還要來得少很多,而且也愈來愈沒有參考價值了。

在Linux系統上,常會使用「free」或是「top」指令來查看作業系統的記憶體資訊。例如:

free -b

node-meminfo

              total        used        free      shared  buff/cache   available
Mem:    33638309888  6050537472  8488792064   185831424 19098980352 26580381696
置換:           0           0           0

其中選項「-b」表示記憶體大小的單位為位元組(byte)。而且可以看到光是記憶體資訊就有分「total」、「used」、「free」、「shared」、「buff/cache」、「available」這幾個欄位,甚至還有「Mem(主記憶體RAM)」和「置換(Swap)」兩列,這比起Node.js內建提供的「totalmem」和「freemem」還要多了很多個項目能參考。

先來看主記憶體的部份,「total」的部份指得就是主記憶體或是的大小,其數值會和Node.js內建的「os」模組的「totalmem」相同。「used」的部份指得是已被使用的記憶體,包含共享記憶體,但不包含被用來做快取和緩衝的部份。「free」是完全沒被使用到的閒置記憶體,其數值會和Node.js內建的「os」模組的「freemem」相同。「shared」是可供不同行程(Process)一起使用的共享記憶體。「buff/cache」是快取和緩衝還有用來管理其他記憶體的Slab快取(Slab Allocator)所佔用的記憶體。「available」則才是作業系統目前真正的「可用記憶體」。

再來是置換空間(Swap)的部份,因為置換空間是把次級儲存器(Secondary Storage)或是外部儲存器(External Storage)作為記憶體使用,速度不快,通常會是在主記憶體不夠用的情況下才會去使用,但著實可以成為作業系統可用記憶體的一部份。因此在計算作業系統真正最高可以配置的可用記憶體大小的時候,會把主記憶體的「available」和置換空間的「free」相加,也就是說,如果行程需要配置的記憶體大小超過這個值的話,作業系統將會因為沒有足夠的記憶體能夠使用而導致當機。

回到Node.js的主題上,那麼要如何使用Node.js來得知這些詳細的記憶體資訊呢?Linux系統的話,可以透過讀取「/proc/meminfo」這個檔案來取得即時的記憶體資訊。順帶一提,「free」和「top」指令在程式實作上其實也是去讀取「/proc/meminfo」再將資訊編排出來。

使用「cat」指令來讀取「/proc/meminfo」檔案:

cat /proc/meminfo

node-meminfo

MemTotal:       32849912 kB
MemFree:         7000712 kB
MemAvailable:   24702036 kB
Buffers:           50048 kB
Cached:         17159880 kB
SwapCached:            0 kB
Active:         19296416 kB
Inactive:        4682504 kB
Active(anon):    6772572 kB
Inactive(anon):   204088 kB
Active(file):   12523844 kB
Inactive(file):  4478416 kB
Unevictable:        1016 kB
Mlocked:            1016 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:               896 kB
Writeback:             0 kB
AnonPages:       5993088 kB
Mapped:          1352728 kB
Shmem:            207672 kB
Slab:            1506992 kB
SReclaimable:    1169828 kB
SUnreclaim:       337164 kB
KernelStack:       25180 kB
PageTables:        99816 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    16424956 kB
Committed_AS:   20239936 kB
VmallocTotal:   34359738367 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
HardwareCorrupted:     0 kB
AnonHugePages:   2236416 kB
ShmemHugePages:        0 kB
ShmemPmdMapped:        0 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:     4365648 kB
DirectMap2M:    29093888 kB
DirectMap1G:     1048576 kB

透過「/proc/meminfo」檔案可以得到非常詳細的記憶體資訊,只要在Node.js中使用檔案相關的模組去讀取「/proc/meminfo」的內容回來,再去解析(parse)有用的資訊即可。當然,也可以直接用底下將要介紹的模組。

node-meminfo

「node-meminfo」是一個使用Node.js 8之後才支援的N-API所開發的模組,使用C語言來讀取Linux作業統中的「/proc/meminfo」所儲存的記憶體資訊。

GitHub:

https://github.com/magiclen/node-meminfo

npm:

https://www.npmjs.com/package/node-meminfo

安裝

直接使用npm指令進行安裝:

npm install --save node-meminfo

用法

初始化

使用「require」函數來引入「node-meminfo」模組。

const meminfo = require('node-meminfo');
取得「/proc/meminfo」所儲存的記憶體資訊

使用模組提供的「get」函數可以完整地取得「/proc/meminfo」所儲存的記憶體資訊,用法如下:

var result = meminfo.get();
// { MemTotal: 33638309888,
//   MemFree: 6813110272,
//   MemAvailable: 25217355776,
//   Buffers: 45658112,
//   Cached: 17956536320,
//   SwapCached: 0,
//   Active: 20752044032,
//   Inactive: 4362379264,
//   'Active(anon)': 7117885440,
//   'Inactive(anon)': 199598080,
//   'Active(file)': 13634158592,
//   'Inactive(file)': 4162781184,
//   Unevictable: 1343488,
//   Mlocked: 1343488,
//   SwapTotal: 0,
//   SwapFree: 0,
//   Dirty: 1826816,
//   Writeback: 0,
//   AnonPages: 6723411968,
//   Mapped: 1297338368,
//   Shmem: 205258752,
//   Slab: 1387651072,
//   SReclaimable: 1089368064,
//   SUnreclaim: 298283008,
//   KernelStack: 23089152,
//   PageTables: 84766720,
//   NFS_Unstable: 0,
//   Bounce: 0,
//   WritebackTmp: 0,
//   CommitLimit: 16819154944,
//   Committed_AS: 18899087360,
//   VmallocTotal: 35184372087808,
//   VmallocUsed: 0,
//   VmallocChunk: 0,
//   HardwareCorrupted: 0,
//   AnonHugePages: 3951034368,
//   ShmemHugePages: 0,
//   ShmemPmdMapped: 0,
//   CmaTotal: 0,
//   CmaFree: 0,
//   HugePages_Total: 0,
//   HugePages_Free: 0,
//   HugePages_Rsvd: 0,
//   HugePages_Surp: 0,
//   Hugepagesize: 2097152,
//   DirectMap4k: 3071623168,
//   DirectMap2M: 26895974400,
//   DirectMap1G: 5368709120 }

這裡要注意的是,模組取得的數值所用的單位是位元組(byte),而不是「/proc/meminfo」原先的KB。

另外,若您像筆者一樣比較習慣「free」指令所提供的資訊格式的話,也可以使用模組提供的「free」函數來取得記憶體資訊,用法如下:

var result = meminfo.free();
// { mem:
//    { total: 33638309888,
//      used: 7504060416,
//      free: 6697676800,
//      shared: 211382272,
//      buff: 45658112,
//      cache: 19390914560,
//      available: 25142546432 },
//   swap: { total: 0, used: 0, free: 0 } }