Macアクティビティモニタの不満をSwiftUI自作で解消!1画面監視&重いアプリ冬眠ツール開発録
Windows から Mac へ移行した方や、長年 Mac を複数台運用している方が一度は感じる小さなストレスが、標準搭載の 「アクティビティモニタ」が CPU・メモリ・エネルギー・ディスク・ネットワークと別々のタブに分断されていること です。「今ファンが回っている原因は CPU なのかメモリ圧迫なのか、どのアプリが何コア分・何スレッド抱えているのか」を 1 つの画面で横並びに俯瞰できません。さらに数年前の Intel Mac では、開いたままの裏ブラウザが CPU を食い続けて動作が重くなりがちです。
結論からお伝えすると、C 言語のシステム API(proc_pidinfo)と SwiftUI の Table を組み合わせて Windows タスクマネージャー風の 1 画面横並び監視アプリ『CoreMonitor』 と、子プロセス群を親アプリ単位で合算してワンクリックで一時冬眠(SIGSTOP)させる『MacChiller』 を AI と自作したことで、新旧 2 台の iMac のリソース管理と静音化が劇的に快適になりました。本記事では、macOS の公開 API で「取れる情報・取れない情報の境界線」と、クラッシュやシステム凍結を防ぐ安全な実装ノウハウを公開します。
-
【Xcode不要でMac専用アプリ自作】Swiftを丸暗記せず『AIを社内常駐PGにする分担術』とApp Sandbox・RAMDiskビルド実践録 : ビルド中間ファイルを 16GB RAM ディスク(
/Volumes/RAMDisk/$USER/swift-build)へ逃がし、内蔵 SSD を一切摩耗させずに数秒でコンパイルする基盤構成です。 -
【実機検証】27インチ5K iMacデュアル要塞の構築録!PRO-G40 SSD 8TB×OWCハブ×Luna Displayの速度低下罠と新型Magic Trackpadが動かない原因を完全解決 : 本記事の検証環境であるメイン機
iMac 20,1(メモリ 64GB)とサブ機iMac 15,1の 5K デュアル運用環境です。 -
【16GB RAMDisk 爆速化術】64GBメモリの余剰でSSD寿命を守りビルドを30秒で終わらせる設計 : 大容量メモリの余剰領域をキャッシュと開発ビルド置き場に変える実践ガイドです。
-
Dexcom G7をMacで常時表示!SwiftUIとSwift Chartsで血糖値アプリ自作録 : デスクワーク中にMacでDexcom G7のグルコース値を常時確認し、座りっぱなしと食後ウォーキングで目標値に戻る時間差を予測するSwiftUIアプリ自作録。日本版Dexcom Share連携の急所を解説します。
-
もち麦の水加減とβ-グルカン・糖質量を1g単位で逆算するSwiftUI炊飯計算Macアプリ自作録 : もち麦(きらりもち等)は水加減を2.5〜3倍に変えると100gあたりの糖質量とβ-グルカンが大きく変わります。この誤差をなくすため、実測重量補正・等分逆算ができる炊飯栄養計算MacアプリをSwiftUIで自作した実践録です。
-
MacのSSD寿命診断(S.M.A.R.T.)アプリをRust×Tauriで自作|Crucial外付け対応録 : 軽量なRust+Tauriで日本語版S.M.A.R.T.(SSD寿命・温度診断)Macアプリを自作。通電5万時間のiMacの「残り寿命170%表示罠」と、外付けCrucial X9 ProのUnknown Drive突破手順を解説します。
-
macOS 標準の「アクティビティモニタ」で CPU タブとメモリタブを何度も行ったり来たりするのに不便を感じている 方
-
Intel 世代の iMac や MacBook をサブ機として大切に使っており、使っていない裏アプリの CPU 負荷やファン回転を抑えてサクサク動かしたい方
-
SwiftUI と macOS のシステムコール(
libproc/ POSIX シグナル)を組み合わせて、自分専用のシステムユーティリティを AI と一緒に作ってみたい 方
なぜMac標準アクティビティモニタは歯痒いのか
macOS 標準の「アクティビティモニタ」は非常に優秀な純正ツールですが、設計思想として「CPU」「メモリ」「エネルギー」が上部のタブで完全に切り離されています。そのため「CPU 使用率が高いアプリが、同時にメモリを何 GB 食っていて、裏で何本のスレッド(並行処理)を走らせているか」を見比べようとすると、その都度タブを切り替えてアプリ名を探し直さなければなりません。
また、macOS(UNIX 系 OS)の CPU 使用率は 「論理 1 コア(1 スレッド)使い切り = 100%」 という基準で表示されます。たとえば 10 コア 20 スレッドの iMac なら最大値は 2,000% になるため、一覧に 250% と出ていても直感的に「いま CPU コアを何個分使っているのか(= 2.5 コア相当)」が頭に入りにくいという弱点もありました。
- CPU・メモリ・スレッドを1画面の横並びテーブルで同時ソートできない
- Chrome等のレンダラー子プロセスが数十個バラバラに並んで合算しづらい
- 「アプリを終了せずに一時冬眠させてCPU 0%にする」ボタンはない
- Windowsタスクマネージャー風にCPU(%+コア数)・メモリ・スレッドを1画面表示
- 列ヘッダークリックで重い順へ即座に並び替え可能(1MB級の超軽量ネイティブ動作)
- 古いMacでは親アプリ単位で子プロセスを一括サスペンド(冬眠)・復帰可能
ほんまそれなんよ!Windows のタスクマネージャーみたいに、タブを切り替えんでも「どのアプリが CPU とメモリを両方食っとるか」を 1 枚のエクセル表みたいに横並びで見たいんよね!
毎日見る計器盤だからこそ、ワンアクション減るだけで快適さがまるで違いますよね!SwiftUI には macOS 12 以降で標準の表コンポーネント(Table)が用意されているので、実は驚くほどスッキリ作れるんです。
Windows風1画面監視『CoreMonitor』の実装
proc_pidinfoでコア数とスレッドを一括取得
1 つ目に作成した CoreMonitor は、現在起動しているアプリケーションごとに 「アプリ名(アイコン付き)」「PID(プロセス ID)」「CPU 使用率(% および消費コア数換算)」「メモリ使用量(MB / GB)」「Threads(同時稼働スレッド数)」 の 5 列を 1 つのテーブルに横並び表示し、どの列見出しをクリックしても昇順・降順ソートできるダッシュボードです。
外部コマンドの ps や top を毎秒サブプロセス起動すると、監視アプリ自身が CPU を食ってしまいます。そこで macOS カーネルの C 言語ライブラリ(<libproc.h> の proc_pidinfo に PROC_PIDTASKINFO を指定)を Swift から直接呼び出し、ナノ秒単位の累積 CPU 時間・物理メモリ常駐サイズ(pti_resident_size)・稼働スレッド数(pti_threadnum)をインプロセスで高速取得する設計にしました。
ここでプログラムの堅牢性として絶対に外せない 境界値バグ対策 があります。proc_pidinfo が返す累積 CPU 時間(pti_total_user + pti_total_system)は UInt64(符号なし 64 ビット整数)ですが、更新タイミングの狭間でプロセスが終了・PID 再利用されたりした場合に万が一 今回の値 < 前回の値 となると、Swift の整数減算は アンダーフロートラップ(EXC_BAD_INSTRUCTION)を起こしてアプリが即死クラッシュ します。必ず current >= previous ? current - previous : 0 の三項ガードを挟んで差分を計算しましょう。
// ProcessMonitor.swift(抜粋:proc_pidinfo直接呼び出し&UInt64アンダーフロー防止ガード)
import Foundation
import AppKit
import Darwin
struct AppResourceRow: Identifiable {
let pid: pid_t
let name: String
let icon: NSImage?
let cpuPercent: Double
var coresUsed: Double { cpuPercent / 100.0 }
let memoryMB: Double
let threadCount: Int
var id: pid_t { pid }
}
final class ProcessMonitor {
private var prevCpuNs: [pid_t: UInt64] = [:]
private var prevTimestampNs: UInt64 = DispatchTime.now().uptimeNanoseconds
private let timebaseInfo: mach_timebase_info_data_t = {
var info = mach_timebase_info_data_t()
mach_timebase_info(&info)
return info
}()
func sampleRunningApps() -> [AppResourceRow] {
let nowNs = DispatchTime.now().uptimeNanoseconds
let elapsedNs = nowNs > prevTimestampNs ? (nowNs - prevTimestampNs) : 1
prevTimestampNs = nowNs
var nextCpuMap: [pid_t: UInt64] = [:]
var rows: [AppResourceRow] = []
for app in NSWorkspace.shared.runningApplications {
let pid = app.processIdentifier
guard pid > 0 else { continue }
var taskInfo = proc_taskinfo()
let size = Int32(MemoryLayout<proc_taskinfo>.stride)
let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, size)
guard ret == size else { continue }
// Mach absolute time をナノ秒へ変換(オーバーフロー回避のためUInt128相当の順序で計算)
let rawTotal = taskInfo.pti_total_user &+ taskInfo.pti_total_system
let numer = UInt64(timebaseInfo.numer)
let denom = max(UInt64(timebaseInfo.denom), 1)
let totalCpuNs = (rawTotal / denom) * numer
nextCpuMap[pid] = totalCpuNs
let prevNs = prevCpuNs[pid] ?? totalCpuNs
// PID再利用や異常値で totalCpuNs < prevNs となっても絶対にクラッシュさせない安全減算
let deltaCpuNs = totalCpuNs >= prevNs ? (totalCpuNs - prevNs) : 0
let cpuPct = min((Double(deltaCpuNs) / Double(elapsedNs)) * 100.0, 6400.0)
let memMB = Double(taskInfo.pti_resident_size) / (1024.0 * 1024.0)
let threads = Int(max(taskInfo.pti_threadnum, 0))
rows.append(AppResourceRow(
pid: pid,
name: app.localizedName ?? "PID \(pid)",
icon: app.icon,
cpuPercent: cpuPct.isFinite ? cpuPct : 0,
memoryMB: memMB.isFinite ? memMB : 0,
threadCount: threads
))
}
prevCpuNs = nextCpuMap
return rows
}
}
Pコア判別やGPU取得ができないmacOSの壁
開発の途中で「せっかくなら CPU・メモリだけでなく、高性能コア(P コア)と高効率コア(E コア)のどちらを使っているかや、アプリごとの GPU 使用率・通信量も全部同じ表に出せないか?」と検証しました。結論から言うと、一般権限で動く自作アプリでは「CPU・メモリ・スレッド数」の 3 点に絞るのが最も合理的 です。
| 取得したい指標 | 一般権限の公開APIでの可否 | 技術的な理由・無理に取得しようとした場合の副作用 |
|---|---|---|
| CPU使用率(消費コア数) | ◎ 取得可能(超軽量) | proc_pidinfo(PROC_PIDTASKINFO)でミリ秒未満で取得可能 |
| 実メモリ使用量・スレッド数 | ◎ 取得可能(超軽量) | 同じく pti_resident_size と pti_threadnum から一括取得可能 |
| Pコア / Eコア別の稼働内訳 | ✕ 一般権限では不可 | sudo powermetrics 等の管理者(root)権限と専用サンプリングが必要 |
| プロセス別のGPU使用率 | ✕ 公開APIなし | 純正アクティビティモニタは Apple 非公開フレームワーク(SkyLight / IOGPU 等)を使用。ioreg 総当たりで代用すると監視ツール自身が激重になる |
| プロセス別の通信量 | △ 管理者権限が必要 | nettop 相当のソケット統計は一般ユーザー権限だけでは制限が多い |
「無理に裏技コマンド(ioreg 毎秒スキャン等)を回してモニターアプリ自身が CPU を 20% 食う本末転倒」を避け、公開 API で軽く取れる 5 列(アプリ名・PID・CPU コア数・メモリ・スレッド数)に割り切ったことで、常駐させても負荷ほぼゼロの軽快な計器盤に仕上がりました。
古いMacを冷やす一括冬眠ツール『MacChiller』
なぜ古いMacで最新ブラウザや動画が重くなるか
メイン機の iMac 20,1(2020 年モデル・10 コア CPU・メモリ 64GB)ではどんな作業も快適ですが、サブ機として動かしている先代の iMac 15,1(Late 2014 モデル) では「数年前まではサクサクだったのに、OS やブラウザが新しくなるにつれて YouTube 再生やタブ切り替えが重くなった」と感じる場面が増えていました。
ハードウェアが壊れたわけではないのに古い Mac が苦しくなる主因は、主に 2 つのソフトウェア肥大化 にあります。
-
高圧縮動画コーデック(VP9 / AV1)の CPU 力業デコード : 昔の Web 動画は軽い
H.264形式が主流でしたが、現在の YouTube 等は高画質なVP9やAV1形式を優先配信します。近年のチップは専用回路(ハードウェアデコーダ)で涼しい顔をして再生しますが、2014 年世代の GPU は非対応のため CPU の力業(ソフトウェアデコード)で毎秒展開する羽目になり、一気に CPU 使用率とファン回転数が跳ね上がります。 -
モダンブラウザの大量マルチプロセス化 : 現在の Google Chrome 等のブラウザはセキュリティ分離のため、タブや拡張機能ごとに
Google Chrome Helper (Renderer)といった独立した子プロセスを 15〜30 個も立ち上げます。見ていない裏ウィンドウの広告スクリプトやバックグラウンド通信がチリツモで CPU コアを奪い続けます。
親アプリ合算とSIGSTOP一括冬眠の安全境界
「タブを全部閉じてアプリを終了させれば軽くなるけれど、調べ物途中のウィンドウ配置は消したくない…」という悩みを解決するために作った 2 つ目のツールが MacChiller(v2) です。
散らばった 15 個の Chrome Helper プロセスを親プロセスの階層(ppid やバンドルグループ)で 「Google Chrome(計 15 プロセス:合計 CPU 120% / メモリ 2.5GB)」 のように 1 行へまとめて合算表示し、横にある [一括 Suspend(冬眠)] ボタンを押すだけで、親プロセスと配下の子プロセス全員に UNIX 標準の一時停止シグナル(SIGSTOP)を送って メモリ上の状態を保ったまま CPU 消費をピタッと 0% に凍結 します。作業を再開したいときは[Resume(復帰)]ボタンを押せば SIGCONT が送られ、0.1 秒で元の画面のまま動き出します。
メインアプリのPIDに紐づくHelper子プロセス群を束ね、合計CPUコア数とメモリ使用量を算出します。
システム必須プロセスを除外した上で、対象アプリと子プロセス群を一時停止しCPU消費を0%にします。
Resume押下時だけでなく、MacChiller自身の終了時(applicationWillTerminate)にも全停止中PIDへ自動でSIGCONTを送って凍結残りを防ぎます。
なお、SIGSTOP は強力ゆえに 絶対に守るべき 3 つの安全ルール があります。
-
システム中枢プロセスと自分自身は絶対に停止対象から除外する :
WindowServer、Finder、Dock、loginwindow、kernel_task、coreaudiod、およびMacChiller自身の PID(getpid())や他ユーザー権限のプロセスを止めると、画面描画やマウス操作ごと Mac が固まります。必ず一般アプリ(activationPolicy == .regularかつ安全除外リスト外)のみにボタンを表示してください。 -
アプリ終了時の「自動解凍(
SIGCONT)」フェイルセーフを入れる : もし対象アプリを冬眠させたままMacChiller自体をうっかり Cmd+Q で閉じてしまうと、冬眠中のアプリが「応答なし(虹色カーソル)」のまま取り残されます。停止させた PID 一覧を保持し、NSApplicationDelegateの終了フックで漏れなくkill(pid, SIGCONT)を送る後始末処理が必須です。 -
iCloud 同期や通話・書き出し中のアプリには使わない : 通信ソケットを張っている最中に長時間
SIGSTOPするとサーバー側からタイムアウト切断されます。「しばらく見ない裏ブラウザや待機中の重いエディタを一時的に黙らせる用途」に絞って使うのが快適運用のコツです。
使っていない裏のブラウザさんに「ちょっとお昼寝しててね〜」って毛布をかけてあげるみたいで賢いでちね!これなら古い iMac さんも涼しい顔で動けるでち!
さらに熱対策を万全にしたい Intel Mac ユーザーの方は、定番のファン回転数制御ツール『Macs Fan Control』で温度が上がりきる前に緩やかにファンを回してあげると、サーマルスロットリング(熱による速度低下)も防げて一石二鳥ですよ!
まとめ:新旧Macを長く快適に使い倒す自作計器盤
OS 標準ツールに「あと一歩、こう見えたらいいのに」という不満があるとき、今の時代は AI を相棒にすれば わずか数 MB でサクサク動く自分専用のネイティブ計器盤 を 1 時間足らずで手に入れることができます。「メイン機では『CoreMonitor』で CPU コア・メモリ・スレッドを 1 画面で気持ちよく俯瞰し、古いサブ機では『MacChiller』で裏プロセスを賢く冬眠させて延命する」——この組み合わせは、手元の愛機を最後まで快適に使い倒す最高の処方箋になります。
ご自身の Mac でも Windows タスクマネージャー風の 1 画面統合モニターを作ってみたい方は、以下のコピペ用プロンプトを最上位 AI モデルへ渡してビルドしてみてください。
※C 言語 API(proc_pidinfo)のメモリレイアウト整合性や UInt64 アンダーフロー防止ガードを正確に書かせるため、必ず 『Gemini 4 Argon』(または同等以上の最上位推論モデル)を選択して実行してください。
以下の記事の設計を参考に、Swift Package Manager(Package.swift / macOS 13以降対象)で動く、Windowsタスクマネージャー風の1画面リソース監視Macアプリ『CoreMonitor』を作成してください。
参照URL: https://lab.chihouzakki.com/blog/review/swiftui-coremonitor-macchiller-process-manager-diy/
【実装ルールと安全要件(厳守)】
1. 外部コマンド(psやtop)のサブプロセス起動は使わず、Darwinの <libproc.h>(proc_pidinfo / PROC_PIDTASKINFO)と NSWorkspace.shared.runningApplications を用いて、2秒周期で「アプリ名+アイコン」「PID」「CPU使用率(%表示および ◯.◯コア相当 の併記)」「実メモリ使用量(MB/GB)」「スレッド数(pti_threadnum)」の5項目を取得してください。
2. SwiftUIの Table と KeyPathComparator を用い、5つの列すべてで見出しクリックによる昇順・降順ソートが可能な1画面ダッシュボードUIにしてください(画面上部にシステム全体の合計稼働アプリ数と更新間隔切替、タイトルバーまたは下部にリビジョン番号を表示)。
3. 累積CPUナノ秒(UInt64)の差分計算時は、PID再利用等で現在値が前回値を下回っても絶対にアンダーフロートラップ(EXC_BAD_INSTRUCTION)を起こさないよう `current >= prev ? current - prev : 0` と `isFinite` ガードを徹底してください。
4. App.init() に NSApplication.shared.setActivationPolicy(.regular) と activate(ignoringOtherApps: true) を実装し、RAMDiskがある環境では /Volumes/RAMDisk/$USER/swift-build/CoreMonitor(無い場合は /tmp/swift-build-$USER/CoreMonitor)を --scratch-path に使う起動スクリプトも添えてください。
5. (任意オプション)もし選択した一般アプリを一時サスペンド(SIGSTOP / SIGCONT)するボタンを追加する場合は、Finder・Dock・WindowServer・loginwindow・自身PID等のシステム重要プロセスを厳格に除外し、アプリ終了時(applicationWillTerminate)に停止中プロセスへ必ず SIGCONT を送って解凍するフェイルセーフを必須実装してください。 お気に入りの Mac の体調を一目で把握して、静かで軽快なデスク環境を楽しんでいきましょう!

▲ 使わない裏アプリはスヤスヤお昼寝させて、Macも涼しく快適運転!