読者です 読者をやめる 読者になる 読者になる

イメージに染まるまで……

Mac,iOSなどAppleの製品についての記事や、セキュリティだったりGTDの記事を書いています。

AppleScriptでメモリ開放アプリを自作する

Mac AppleScript

はじめに

OSX Marvericks以降、メモリ圧縮機能がOSXに付いて、メモリ開放は要らなくなるとばかり思っていました。

実際、私個人としては全く使わなくなりましたし、メモリが逼迫したところでメモリ圧縮の効果なのか、動作が遅くなるということが無くなりました。

それでもまだ、メモリ開放アプリがAppStoreに出ますし、話題になります。どうしてそこまでメモリ開放が必要なのか疑問ですが、まだ需要があるということでしょう。

もちろん私自身も、Marvericksまではメモリの管理に悩んでおり、メモリ開放はしていました。その時に利用していたのが、先人たちの弛まぬ努力によって出来たAppleScript製のメモリ開放アプリです。

簡単にメモリ開放のアプリが手に入るからこそ、かえって今、もう一度AppleScript製のアプリを作ってもいいのではないか、と考えて、現状のYosemite環境で作り直してみました。

もちろん、自作すれば無料ですしね。

昔からあったものを見ながら、どれが今でも有効かを検討しながら、Yosemiteに対応したAppleScriptにしてみたいと思います。

結構長い記事になるので、時間があるときに読んでください。

目次

先人たちの歩み
必要なコマンド
Yosemiteに合わせる
常駐化
おわりに
参考サイト

環境

(Mac) OSX 10.10.3

先人たちの歩み

私の知っている限り、先人たちの歩みは、スワップファイルを防止しようという試みからでした。

うむらうすさんは、スワップファイルをなくそう4という記事で、AppleScript製のアプリを作られました。

ここでは、以下のような動作で軽量化をしていました。

  • Finderの再起動
  • Dockの再起動
  • Weekly Maintenance Scriptの実行

この記事の時点では、Mac OSX 10.4 Tigerに対応していましたが、この後、Leopard, Snow Leopardと対応を続けました。

Weekly Maintenance Scirptではなく、アクセス権の修復を利用したり、locate databaseの更新のコマンドを利用していました。

また、常駐してメモリの状態を監視するようになったり、面白かったです。

その後、Xcodeを入れている環境で使うことのできるpurgeコマンドを利用した同様のメモリ解放アプリを作成なさったりもしました。


これと並行して、現在ではすでに公開されていませんが、ものかのさんがLibera Memoryというアプリがありました*1

こちらでは、コマンドにduコマンドを利用しており、これを利用したAppleScript製のアプリもあります。

Mac:メモリ解放アプリ | Macとかの雑記帳
メモリ解放と、Finder、Dock、メニューバーの再起動をするアプリです。Mac用のメモリ解放アプリはいくつかありますが、メニューバーの再起動までしてくれるものが見つからなかったので自作したものです。


さらに、こんな試みもあります。

メモリを割とガッツリ解放するAppleScriptを作る過程 - ザリガニが見ていた...。

これは、purgeとduとを両方使っていて、アクセス権の修復も行っています。

この記事の前のメモリを解放してスワップ発生を抑える方法は、OSXのメモリ管理の仕組みの一端を知ることができるので、読んでおくと楽しいかもしれません。

必要なコマンド

長くなりましたが、先人たちの知恵をここまで見てきました。これらの歩みを踏まえて、重要なものをまとめてみましょう。

まず、今まで使われていたコマンドです。メモリ解放はほとんど、AppleScriptからTerminalで使うコマンドを呼び出しているのです。

  • ディスクの修復 diskutl ripairPermissions /
  • Finderの再起動 killall Finder
  • Dockの再起動 killall Dock
  • locate databaseのアップデート sudo /usr/libexec/locate.updatedb
  • duコマンド du -sx / &> /dev/null & sleep 25 && kill $!
  • purgeコマンド purge

これらを組み合わせて、メモリを解放していました。

ここから、誰でも使えるAppleScriptに仕上げてゆくことになります。

私は今まで、purgeとduコマンドとディスクの修復の有効性しか試していませんでした。

Finderの再起動は、ファイルの書き込みを中断させたり、マウントしているディスクをアンマウントしてしまったりするので、やめていたのです。Dockも特に考えていませんでした。*2

また、locate databaseはOSXのバックグラウンドメインテナンスタスクでアップデートされるので、それは任せておいてもいいかと考えていました。AppleScriptでいつでも呼び出すことを考えると、sudoで管理者権限を出すのも嫌だな、と思っているのもあります。

条件は、管理者権限を出さないものということにします。

さらに、どうやらpurgeコマンドよりもduコマンドの方がよくメモリを解放してくれるようなので、duコマンドを利用します。

また、purgeはXcodeを入れていないと使えないので、duを優先します。

アクセス権の修復は、確かに多少の効果があるのですが、処理の方が重いので、外しました。*3

これで、組んでみましょう。

Yosemiteに合わせる

まず、このスクリプトの素体は先ほどもあげましたが、

Mac:メモリ解放アプリ | Macとかの雑記帳
メモリ解放と、Finder、Dock、メニューバーの再起動をするアプリです。Mac用のメモリ解放アプリはいくつかありますが、メニューバーの再起動までしてくれるものが見つからなかったので自作したものです。

こちらです。変更点は、一部コマンドラインで返された値が変わっていたことに対応したことです。

かつ、処理の最初のアラートと、終わった時の通知をGrowlではなく、Mavericks以降、誰でも使える通知センターに変更しました。

--最初に開始したことを示す通知。必要ないなら削除。
display notification "Prosesseing……(about 30 second)" with title "Memory Release Script"
-- 欲しければ、以下のFinderの再起動や、Dockの再起動、メニューバーの再起動のコメントを外す。最初のハイフンふたつを削除すれば良い。
-- do shell script “killall Finder” -- Finderの再起動
-- do shell script “killall SystemUIServer” -- メニューバーの再起動
--delay 2 -- ちょっと待つ
-- do shell script “killall Dock“ -- Dockの再起動

-- メモリをduコマンドを使用して解放する
do shell script "du -sx / &> /dev/null & sleep 25 && kill $!"

delay 2

property rt : return as text

    on memStatus()
    -- PhysMemで取得できる値が変更されていたので、Yosemiteの環境で動くように調整
    set memoryStatus to do shell script "top -l 1 | head -10 | grep PhysMem"

    set usedMem to word 2 of memoryStatus
    set wiredMem to word 4 of memoryStatus
    set  unusedMem to word 6 of memoryStatus

    -- 以降はそのまま利用できた
    set swapusage to do shell script "sysctl vm.swapusage"
    set theSwap to "Swap : " & word 7 of swapusage & " (" & word 4 of swapusage & ")"

    set grepLine to do shell script "top -l 1 | head -10 | grep VM"
    set pageins to (((round (word 7 of grepLine as number) / 4096 * 100) / 100 * 16) as text) & "M"
    set pageouts to (((round (word 10 of grepLine as number) / 4096 * 100) / 100 * 16) as text) & "M"

    -- アラートに返す値を上の変更に合わせた
    return "使用中:" & usedMem & "(うち固定:" & wiredMem & ")" & rt & "空き:" & unusedMem & rt & theSwap & rt & "Page Ins : " & pageins & rt & "Page Outs : " & pageouts & rt
end memStatus

-- 必要ないなら、下の完了したことを示す通知を削除
display notification "Memory Released" with title "Memory Release Script" sound name "Purr"

display alert "Memory Status" message my memStatus()

この状態だと、こういう動作をします。

呼び出すと、開始を通知します(ここでは、Quicksilverを使ってスクリプトを走らせたので、アイコンがQuicksilverになっています)。

f:id:nya-0:20150427094302p:plain

しばらくすると、メモリの解放の完了を知らせて、現在のメモリ容量を表示します。

f:id:nya-0:20150427094312p:plain

これをコピペした状態ですと、Finderの再起動、Dockの再起動、メニューバーの再起動は全て処理から外しています。好みで最初のハイフンを削除すると使えるようになりますから、自分の判断で利用してください。

(Apple)Scriptエディターにコピーして、コンパイルしてから保存してください。

常駐化

さらに先人の知恵を借りて、常駐させてみましょう。

先ほど修正した時に、メモリの空いている量をそのまま取れたので、vm_statではなくPhysMemからそのまま取ります。

さらに、動き始める前に確認を入れるようにします。

最後に、メモリの下限を決めて、コンパイル!……とはやらせてもらえませんでした。

先ほど挙げたメモリを割とガッツリ解放するAppleScriptを作る過程 | ザリガニが見ていた…。にあるように、個々の処理をユニット化して、場合分けの処理と、実際の処理を別々に変えました。

これで動くようにはなったのですが、キャンセルをするとエラーメッセージが出ます。正常に止めたのにこれでは面倒です。どうにかならないかと思って探してみると、Appleデベロッパーセンターに回避方法がありました。display dialogの部分にサンプルコードがあります。*4

これを反映させて、書き直しました。それが以下のものになります。

property rt : return as text

-- 繰り返し処理
on idle {}
    if my freeMem < 500 then -- メモリ残量の設定
        my main()
    end if
    return 600 -- 起動する間隔の設定
end idle

-- メモリの状態をチェックする本体
set currentMem to do shell script "top -l 1 | head -10 | grep PhysMem"
set freeMem to word 6 of currentMem

-- 再度アプリケーションを立ち上げる操作をした場合
on reopen {}
    my main()
end reopen

-- メモリ解放をするメインのスクリプト
on main()
    set userCanceled to false
    try
        activate
        set dialogResult to display dialog "現在の空き容量は" & my freeMem & "です。本当にメモリ解放を実行してよろしいでしょうか?もし実行した場合、30秒ほどかかります。" with icon note buttons {"実行する", "今はしない"} default button "実行する" cancel button "今はしない" giving up after 15
    on error number -128
        set userCanceled to true
    end try

-- キャンセルを押した場合 
if userCanceled then
    display notification "Canceled by user" with title "Memory Release Script" sound name "Funk"

        -- 実行する場合
    else if button returned of dialogResult is "実行する" then
        display notification "Prosessing……" with title "Memory Release Script" sound name "Funk"
        delay 2
        -- 欲しければ、以下のFinderの再起動や、Dockの再起動、メニューバーの再起動の部分のコメントを外す。最初のハイフンふたつを削除すればよい。
        -- do shell script "killall Finder" -- Finderの再起動
        -- do shell script "killall SystemUIServer" --メニューバーの再起動
        -- delay 2 --ちょっと待つ
        -- do shell script "killall Dock" --Dockの再起動

        -- メモリをduコマンドを使用して解放する
        do shell script "du -sx / &> /dev/null & sleep 25 && kill $!"
        delay 2
        -- 必要ないなら、下の完了したことを示す通知を削除
        display notification "Memory Released" with title "Memory Release Script" sound name "Purr"
        -- メモリの状況を知らせる
        display alert "Memory Status" message my memStatus()

        -- 15秒で自動的にキャンセルされる     
    else if gave up of dialogResult then
        display notification "Canceled by time" with title "Memory Release Script" sound name "Funk"
    end if
end main

on memStatus() -- 自動的に動かそうとした場合、main()に入れておくと動作しなかったため、分けた。
    -- PhysMemで取得できる値が変更されていたので、Yosemiteの環境で動くように調整
    set memoryStatus to do shell script "top -l 1 | head -10 | grep PhysMem"

    set usedMem to word 2 of memoryStatus
    set wiredMem to word 4 of memoryStatus
    set unusedMem to word 6 of memoryStatus

    -- 以降はそのまま利用できた
    set swapusage to do shell script "sysctl vm.swapusage"
    set theSwap to "Swap : " & word 7 of swapusage & " (" & word 4 of swapusage & ")"

    set grepLine to do shell script "top -l 1 | head -10 | grep VM"
    set pageins to (((round (word 7 of grepLine as number) / 4096 * 100) / 100 * 16) as text) & "M"
    set pageouts to (((round (word 10 of grepLine as number) / 4096 * 100) / 100 * 16) as text) & "M"

    -- アラートに返す値を上の変更に合わせた
    return "使用中:" & usedMem & "(うち固定:" & wiredMem & ")" & rt & "空き:" & unusedMem & rt & theSwap & rt & "Page Ins : " & pageins & rt & "Page Outs : " & pageouts & rt
end memStatus

これで、無事Yosemite環境でも動きます!

さて、動きを見てみましょう。

メモリの空き容量が、デフォルトでは500MBを下回ると起動します。あるいは、10分ごとに起動します。

一応本当に起動するかを尋ねますので、決定してください。現在のメモリの空き容量が表示されますので、必要ないと思ったらキャンセルをしてください。

f:id:nya-0:20150427094339p:plain

上と同じく処理を開始すると、通知します。また、キャンセルをしたときも通知します。邪魔と感じる方もいらっしゃるかもしれませんが、キャンセルを正常に処理するために入れたので、ご容赦を。

メモリの解放が終了すると、やはり完了の通知と、メモリの状態を表示します。こちらのスクリプトでは、処理の前後の空きメモリ容量がスクリプトだけで分かります。

f:id:nya-0:20150427094400p:plain

上のスクリプトは任意なので、メモリの状況が分かっていることを前提にしているので、省きました。

また、立ち上げている最中にDockのアイコンをクリックしたり、Finderから呼び出すと、メモリの解放を呼び出すことができます。これはAppleScriptのreopenの処理です。間違ってクリックしたときの動作を防ぐ、フールプルーフの意味もあります。

これもFinderの再起動、Dockの再起動、メニューバーの再起動は処理から外しています。これも好みで手を加えてください。

また、メモリの残量の設定や、起動する間隔の設定はデフォルトでは各々500MB以下、10分ごとにしてありますが、好みで設定しなおしてください。

このスクリプトはアプリケーション形式で保存する際に、「ハンドラの実行後に終了しない」というチェックを入れないと常駐しませんので、注意してください。

f:id:nya-0:20150427094418p:plain

おわりに

いかがでしたでしょうか。

たくさんの記事が、このスクリプトを書く助けになりました。今までお世話になったのをお返しするようなつもりで、Yosemiteに対応するこのコマンドを書きました。

お役に立てば嬉しいです。

本当に、感謝です!

参考サイト

うむらうす

うむらうすさんのサイト。以下のページを主に参照しました。RSSでもいつも読ませていただいています。

スワップファイルをなくそう4 - Release Memory2 (for Tiger)

残り空きメモリ監視AppleScript「Freemem Monitor」作成


わかばマークのMacの備忘録

メモリを細かく表示する部分を参考にさせていただきました。

Task3 / タスクの設定例(1)


ものかの | 豆大福おいしい

LiberaMemory以外にもお世話になったアプリが……感謝です。


ザリガニが見ていた...。

以下の記事を参考にスクリプトを書きました。感謝です!

メモリを解放してスワップ発生を抑える方法

メモリを割とガッツリ解放するAppleScriptを作る過程


Macとかの雑記帳

以下のページを主に参考にしました。感謝です!

Mac:メモリ解放アプリ

Macでメモリを解放するには、purgeよりduを使った方がいい?


Mac Developer LibraryAppleScript Language Guideから、display dialogのサンプルを見て書きました。

また、tryでエラーを無視することもできることを覚えたのは、鳶嶋工房(Tobishima-Factory)さんのAppleScriptの入門記事、エラーに備えようでした。感謝です!AppleScriptの基礎を覚えたのはここだった……

*1:よく覚えているのですが、グラフで解放前後の表示をしてくださったりと至れり尽くせりのアプリでした。

*2:ただ、Finderの再起動とDockの再起動は結構効果があって、特に問題ない時であれば再起動すると、結構動きが軽くなります。3〜4日起動しっぱなしだった状態でFinderを再起動すると、数百MB占有していたのを一気に解放するのも確認しました。

*3:完了後には確かにメモリが解放されています。しかし、ディスクのアクセス権の修復中は少々メモリを占有しますし、多少時間のかかる処理なので候補から外しました。

私の環境で行ったところ、6.78GB空きが完了後には6.00GBまでメモリの空きが増えました。しかし、それまでの間に占有量が7GBを超えます。他のコマンドよりも時間がかかるのもネックです。

*4:tryから始めれば無視もできるのですが、ちょっと挑戦してみました。