二度寝スリープでメモリをガッツリ解放しておく

最近はMacBookの睡眠(スリープ)について、いろいろと調べることが多かった。

その過程で、スリープにはいろいろなモードがあることが分かった。推奨されているのは3つのモード。hibernatemode 0 or 3 or 25。

最初は深く考えずにhibernatemode 25してみる。スリープさせると電源がオフになり一瞬あせるが、電源ボタンを押して暫くすると直前の作業環境に復元される。つまり、メモリをハードディスクに退避させて、電源を完全にオフにするスリープなのだ。なるほど、と分かった気になっていた。

それにしても、0、3に続いて、いきなり25!このhibernatemode 25だけ、異質な感じがするのは明白だろう。一体なぜ25なのか?昔はそんなモードはなかったはず。(確か、0、3、1だった気がする)一旦気になり始めると、とっても気になる。その答えはマニュアル(man pmset)にあった。

SAFE SLEEP ARGUMENTS
hibernatemode takes a bitfield argument defining SafeSleep behavior. Passing 0 disables SafeSleep altogether, forcing the computer into a regular sleep.

  • ____ ___1 (bit 0) enables hibernation; causes OS X to write memory state to hibernation image at sleep time. On wake (without bit 1 set) OS X will resume from the hibernation image. Bit 0 set (without bit 1 set) causes OS X to write memory state and immediately hibernate at sleep time.
  • ____ __1_ (bit 1), in conjunction with bit 0, causes OS X to maintain system state in memory and leave system power on until battery level drops below a near empty threshold (This enables quicker wakeup from memory while battery power is available). Upon nearly emptying the battery, OS X shuts off all system power and hibernates; on wake the system will resume from hibernation image, not from memory.
  • ____ 1___ (bit 3) encourages the dynamic pager to page out inactive pages prior to hibernation, for a smaller memory footprint.
  • ___1 ____ (bit 4) encourages the dynamic pager to page out more aggressively prior to hibernation, for a smaller memory footprint.

We do not recommend modifying hibernation settings. Any changes you make are not supported. If you choose to do so anyway, we recommend using one of these three settings. For your sake and mine, please don't use anything other 0, 3, or 25.

  • hibernatemode = 0 (binary 0000) by default on supported desktops. The system will not back memory up to persistent storage. The system must wake from the contents of memory; the system will lose context on power loss. This is, historically, plain old sleep.
  • hibernatemode = 3 (binary 0011) by default on supported portables. The system will store a copy of memory to persistent storage (the disk), and will power memory during sleep. The system will wake from memory, unless a power loss forces it to restore from disk image.
  • hibernatemode = 25 (binary 0001 1001) is only settable via pmset. The system will store a copy of memory to persistent storage (the disk), and will remove power to memory. The system will restore from disk image. If you want "hibernation" - slower sleeps, slower wakes, and better battery life, you should use this setting.

Please note that hibernatefile may only point to a file located on the root volume.

http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/pmset.1.html

英語は苦手なのだが、自分の理解を書いておく。

  • つまり、hibernatemodeは8ビットの2進数として、各桁に意味を持たせてある。
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 =10進数
0 0 0 0 0 0 0 0 =0
0 0 0 0 0 0 1 1 =3
0 0 0 1 1 0 0 1 =25
  • bit 0:1=ハイバネーション モード(=冬眠モード。スリープでメモリの状態をハイバネーション イメージ(/var/vm/sleepimage)に書き出し、スリープ解除でハイバネーション イメージから復元する)
  • bit 1:1=バッテリーレベルが低下するまで電源を維持する。0=すぐに電源をオフにする。(bit 0が1の場合と連動して意味がある)
  • bit 3:1=ハイバネーションする前に、メモリの退避エリアをなるべく小さくするため、インアクティブなメモリ領域のページアウトを促進する。0=特に何もしない。
  • bit 4:1=ハイバネーションする前に、メモリの退避エリアをなるべく小さくするため、より積極的にページアウトを促進する。0=特に何もしない。


よって、25=ページアウトを促進して確保中のメモリ領域を小さくしてから、すぐに電源をオフにするハイバネーション モード。

  • なんと、たまにちゃんとマニュアルを読むと、とんでもなく衝撃的なことが書いてある!
  • つまり、メモリの解放を促進してからスリープするというのだ。


ちなみに、過去においては...

実験

  • 最初のメモリの状態。

  • hibernatemode 1でスリープ・スリープ解除したメモリの状態。

  • ほとんど変化なし。
  • hibernatemode 25でスリープ・スリープ解除したメモリの状態。

  • ガッツリ解放された!

所感

  • hibernatemode 25 でスリープすると、メモリはガッツリ解放される。
  • これは、du -sx /、diskutil verifyPermissions /、purge(メモリを解放してスワップ発生を抑える方法)に続く第四の方法で、メモリ解放の効果は最大だった。
    • hibernatemode 25のスリープ解除してから、du・duskutil・purge等のコマンドを実行しても、メモリ解放の効果は全くなかった。
  • 但し、ハイバネーションモードの変更は推奨しないと、man pmsetに書いてある。
    • どんな変更もサポートしないと。
    • 変更するなら、以下3つのうちのどれかを推奨しているらしい。
    • くれぐれも、0・3・25以外を使わないようにしてくださいと。
  • また、自分のMacBook環境において、スリープに30秒、スリープ解除に60秒くらいの時間がかかってしまう...。
    • 初代MacBookだと、スリープ・スリープ解除とも、もっと素早いのだけど。(搭載メモリの違いか?2Gと4Gの違い?)


人もMacも睡眠が大事。ぐっすり眠れば、最大のパフォーマンスを発揮する。小笠原気団の真夏日&熱帯夜な攻撃と節電ブームに負けないで、ぐっすり眠りたい。

二度寝スリープでメモリを解放するAppleScript

  • hibernatemode 25のスリープでメモリがカッツリ解放されることは分かったのだが、完全に電源がオフになってしまうので、Wake on Demandなサーバーとしても機能しなくなってしまうのは残念。
  • ならば、hibernatemode 25のスリープ解除を待って、hibernatemode 0のスリープをやり直せば良いのだが、自分のMacBookではhibernatemode 25のスリープ・スリープ解除にえらく時間がかかる...。

待ちきれない!

  • ならばいつものAppleScript(シェルコマンドと組み合わせて)で、ゴニョゴニョやってみた。
  • 手順はシンプル。
    • スリープ解除の予約設定をして、hibernatemode 25でスリープする。
    • 予約時間に目覚めたら、デフォルトのhibernatemode(0 or 3)で再スリープする。


property pw : ""

my save_password() my double_sleep()


on save_password() if pw = "" then
activate
display dialog "管理者パスワードを入力してください。" default answer "" with hidden answer
set pw to result's text returned
end if
end save_password

on double_sleep() "(s=`date +\"%s\"`; s=`expr $s + 60`; h=`pmset -g|grep hibernatemode|cut -f2`; pmset schedule wake \"`date -r $s +\"%D %T\"`\"; pmset -a hibernatemode 25; pmset sleepnow; sleep 60; pmset -a hibernatemode $h; sleep 120; pmset sleepnow;) >& /dev/null &"
do shell script result password pw with administrator privileges
end double_sleep
  • ちなみに、シェルコマンド中のsleep 60、sleep 120でなぜ待機するのか?
    • hibernatemode 25のスリープ処理中でも、sleep 60で待機しておかないとコマンドが次々と処理されてしまうので。
    • あるいは、sleep 120ぐらいの待機時間を取っておかないと、再スリープした後に、勝手に目覚めてしまうので。
  • 環境によっては、待機時間はもっと短くてもちゃんと動作すると思う。


これで、メモリも解放され、Wake on Demandも有効な欲張りなスリープが実現できた!

  • 席を離れる時に二度寝スリープを実行しておけば、幸せになる。
  • Quicksilverでcommand-option-F12にショートカットを割り当てた。
  • ちなみに、commnad-option-Eject = 通常のスリープ。その隣だ。


コマンドの中身について

  • AppleScriptの文字列としてシェルスクリプトを書いているので、ダブルクォートがエスケープ(web上は半角¥になっているが、本来は半角\である)され読みにくくなっている。
  • 実行される段階では、以下のシェルスクリプトとして解釈されているはず。
( #1
s=`date +"%s"`; #2 変数sに現在の時刻(1970/1/1からの積算秒数)を保存します。
s=`expr $s + 60`; #3 変数sに60を加算します。
h=`pmset -g|grep hibernatemode|cut -f2`; #4 変数hに現在のhibernatemodeを保存します。
pmset schedule wake "`date -r $s +"%D %T"`"; #5 スリープ解除時間を予約します。
pmset -a hibernatemode 25; #6 hibernatemode 25を設定します。
pmset sleepnow; #7 スリープします。
sleep 60; #8 60秒待機します。
pmset -a hibernatemode $h; #9 元のhibernatemodeに戻します。
sleep 120; #10 120秒待機します。
pmset sleepnow; #11 再びスリープします。
) >& /dev/null & #12 括弧()内のコマンドはバックグラウンドで処理し、返り値はすべて破棄します。

さらに詳細に見ていくと...

#2、#3
  • `コマンド` で囲まれた中身は、シェルコマンドとして実行した結果で置き換えられる。
  • つまり、AppleScriptでいう Run Script "コマンド" と同等なのだ。
  • 実際に何に置き換えられるかは、`コマンド` 内のコマンドをコピーして、ターミナルで実行してみると一目瞭然。
  • 最終的に、変数sには現時点から60秒後の時刻が、1970/1/1からの積算秒数で代入されるのだ。
  • これを、このあとスリープを解除する際の予約時刻に利用する。
#4
#7、#8、#10、#11
  • sleep 60 は、シェルコマンドに60秒待機させるコマンド。AppleScriptでいうDelay 60と同等なのだ。
  • pmset sleepnow は、MacBookにスリープしてもらうコマンド。
  • どちらもsleepが含まれていて紛らわしいが、まったく意味が違うのだ。
#12