LoginSignup
0
2

More than 1 year has passed since last update.

macOSのメモリ使用状況を常に表示する

Last updated at Posted at 2022-02-08

はじめに

この記事を読むとできること

macOSのそのときどきのメモリの使用状況を、確認することができます。→次に買うMacBookに必要なメモリ搭載量を推定できます。

ここにいたった背景

MacBookを買い換えようとおもいました。32GB必要か、16GBで済むのか、ここが大きな悩みどころであります。そこで、実際の使用量をリアリタイムで知りたいなあと。もちろんアクティビティモニタを見ればわかりますが、いちいちアプリを切り替えたり、ウィンドウを開くのはいかにもめんどうくさいです。メニューバーに常時表示していれば、目の端でこまめにチェックできます。

動きの概要

ツールバーに、使用済みメモリを表示します。クリックすると詳細データをプルダウンで表示します。

詳細

1. SwiftBarのインストール

SwiftBarは、macOSのメニューバーのカスタマイズツールで、スクリプトの標準出力をメニューバーに出してくれます。

SwiftBarのリリースページから最新版をzipダウンロードし、任意のフォルダに置き、起動します。プラグインフォルダを指定するように言われるので、適当なフォルダを指定します。わたしはユーザのホーム(~/)下にフォルダをおきました。

ご参考:macOSのメニューバーにお気に入りのニュースを表示 > SwiftBarのインストール - Qiita

2. Pythonのインストール

以前に入れたので、もうやらなくて済みました。

macOSのメニューバーにお気に入りのニュースを表示 > 2. Pythonのインストール - Qiita

3. プラグインの作成

プラグインフォルダにmemusage.60s.pyというPythonスクリプトを保存します。60sは「60秒ごとに更新」という意味なので、好みの間隔でよいです。

psコマンドとvm_statコマンドからメモリ使用状況を拾っています。

memusage.60s.py
#!/usr/bin/env python
import subprocess
import re

# Get process info
ps = subprocess.Popen(['ps', '-caxm', '-orss,comm'], stdout=subprocess.PIPE).communicate()[0]
vm = subprocess.Popen(['vm_stat'], stdout=subprocess.PIPE).communicate()[0]

# Iterate processes
processLines = ps.split('\n')
sep = re.compile('[\s]+')
rssTotal = 0 # kB
for row in range(1,len(processLines)):
    rowText = processLines[row].strip()
    rowElements = sep.split(rowText)
    try:
        rss = float(rowElements[0]) * 1024
    except:
        rss = 0 # ignore...
    rssTotal += rss

# Process vm_stat
vmLines = vm.split('\n')
sep = re.compile(':[\s]+')
vmStats = {}
# for row in range(1,len(vmLines)-2):
for row in range(1,len(vmLines)-1):
    rowText = vmLines[row].strip()
    rowElements = sep.split(rowText)
    vmStats[(rowElements[0])] = int(rowElements[1].strip('\.')) * 4096

memApp = ( 
    vmStats["Pages active"] 
    + vmStats["Pages speculative"] + vmStats["Pages throttled"] )
memWird = vmStats["Pages wired down"]
memComp = vmStats["Pages occupied by compressor"]
memUsed = memApp + memWird + memComp
memCash = ( vmStats["File-backed pages"] + vmStats["Pages purgeable"] )
memSwap = ( vmStats["Swapouts"] - vmStats["Swapins"] )
b2gb = 1/1024./1024./1024.

# Close to the value of Activity Monitor
print ':memorychip:%.2f GB'  % ( memUsed * b2gb )
print '---'
print 'App mem:\t%.2f GB'    % ( memApp  * b2gb )
print 'Wired down:\t%.2f GB' % ( memWird * b2gb )
print 'Compressed:\t%.2f GB' % ( memComp * b2gb )
print '---'
print 'Cashed Files:\t%.2f GB' % ( memCash * b2gb )
print 'Swap Used:\t%.2f GB'  % ( memSwap * b2gb )

# keep the value from ps
print '---'
print 'from ps:\t\t%.1f GB'  % ( rssTotal/1024./1024./1024. )

# for debug
print '---'
print 'free:\t\t\t%.2f GB'  % ( vmStats["Pages free"]/1024/1024/1024.0 )
print 'active:\t\t%.2f GB'  % ( vmStats["Pages active"]/1024/1024/1024.0 )
print 'inactive:\t\t%.2f GB'  % ( vmStats["Pages inactive"]/1024/1024/1024.0 )
print 'speculative:\t%.2f GB'  % ( vmStats["Pages speculative"]/1024/1024/1024.0 )
print 'throttoled:\t%.2f GB'  % ( vmStats["Pages throttled"]/1024/1024/1024.0 )
print 'wired down:\t%.2f GB'  % ( vmStats["Pages wired down"]/1024/1024/1024.0 )
print 'purgeable:\t%.2f GB'  % ( vmStats["Pages purgeable"]/1024/1024/1024.0 )
print 'reactivated:\t%.2f GB'  % ( vmStats["Pages reactivated"]/1024/1024/1024.0 )
print 'purged:\t\t%.2f GB'  % ( vmStats["Pages purged"]/1024/1024/1024.0 )
print 'file-backed:\t%.2f GB'  % ( vmStats["File-backed pages"]/1024/1024/1024.0 )
print 'anonymous:\t%.2f GB'  % ( vmStats["Anonymous pages"]/1024/1024/1024.0 )
print 'stored comp:\t%.2f GB'  % ( vmStats["Pages stored in compressor"]/1024/1024/1024.0 )
print 'occpd comp:\t%.2f GB'  % ( vmStats["Pages occupied by compressor"]/1024/1024/1024.0 )
print 'decompress:\t%.2f GB'  % ( vmStats["Decompressions"]/1024/1024/1024.0 )
print 'compress:\t%.2f GB'  % ( vmStats["Compressions"]/1024/1024/1024.0 )
print 'pageins:\t\t%.2f GB'  % ( vmStats["Pageins"]/1024/1024/1024.0 )
print 'pageouts:\t%.2f GB'  % ( vmStats["Pageouts"]/1024/1024/1024.0 )
print 'swapins:\t\t%.2f GB'   % ( vmStats["Swapins"]/1024/1024/1024.0 )
print 'swapouts:\t%.2f GB'  % ( vmStats["Swapouts"]/1024/1024/1024.0 )

# keep the value from ps以下、および# for debug以下はなくてもいいです。

psコマンドで拾っているほうはアクティビティモニタとずいぶんずれるので、参考程度に表示していますが、なくしてもいいかもです。

オリジナルのコードは下記ですが、ほしい数字とちょっと違ったので、改良しました。
xbar-plugins/memusage.5s.py at main · matryer/xbar-plugins · GitHub

補足情報

macOSのメモリ管理

macOSのメモリを、ざっくりと図にするとこんなかんじになります。

macOS(unix)のメモリ管理のポイントは

  • OSは、アプリが要求するメモリ枠を、まず確保してしまう
  • 確保枠のなかで、実メモリに割り当てるか、圧縮するか、スワップするかはOSが判断する

ということかなとおもいます。また、ここが混乱しやすいところと思うのですが、仮想メモリという単語には2つの意味があります。

  • アプリのためにOSが(仮想的に)確保しておく枠
  • 物理メモリで足りなくなったぶんディスク上に(仮想的に)作るメモリ

macOSの場合は前者で、「とりあえず確保しておく枠」のこと。Windowsでは後者で、これはmacOS(unix系)ではスワップと呼ばれます。

参考リンク:

アクティビティモニタ項目とvm_stat項目の対応

ピタっとは数字があわないのですが、だいたいの対応は下記の通りです。

アクティビティモニタ vm_stat
使用済みメモリ アプリケーションメモリ Pages active
Pages speculative
Pages throttled
確保されているメモリ Pages wired down
圧縮 Pages occupied by compressor
キャッシュされたファイル File-backed pages
Pages purgeable
スワップ使用領域 Swapouts - Swapins

おわりに

macOS(unix)のメモリ管理の考えかたをふわっとしか理解しておらず、仮想メモリという言葉に違う意味があることを知らず、アクティビティモニタの算出ロジックが見つからなかったので、この三重苦で意外と苦労しました。

アクティビティモニタをコマンドラインで起動できて出力を取れれば簡単だったんですけれど、まあそういうわけにもいかず。

アクティビティモニタとvm_statの対応は、各種記事とman vm_statを調べてあたりをつけ、2週間ぐらい数値を比較して検証しました。間違いがあるとご指摘いただけるとうれしいです。ま、ビシッと一致はしていないのですが、「ふだん使っているメモリ容量を把握しておく」目的にはつかえるぐらいの精度かなとおもっております。

環境

ここで書いていることは、下記のバージョンで実施しました。

  • SwiftBar 1.4.2(386)
  • macOS Monterey 12.1
  • MacBook Pro (13-inch, 2020, Four Thunderbolt 3 Ports)
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2