![]() |
AppleScript to display Safari's memory usage
As many of you will have noticed, Safari often seems to get itself into a state where it starts to take a lot of memory. I'm not yet convinced that this is technically a "memory leak" (where an app has lost track of memory that it allocated) since it might just be that Safari is misguidedly caching too much info about previously viewed pages or whatever.
But it certainly is the case that at times Safari starts to take much more of my precious RAM than I want it to. I don't mind too much if it is taking 100 MB if I've been looking at graphically-rich pages, but I expect it to be a lot less than that most of the time. And if I notice that Safari is taking up much more than 100 MB of real memory (e.g. by looking at Activity Monitor), I will quit and re-launch it in order to conserve RAM. This post presents an AppleScript that monitors the RAM usage of Safari and displays it (in megabytes) in the title of the frontmost window. The AppleScript pops up an alert dialog if the Safari memory usage exceeds 150 MB. There are two versions of this AppleScript:
However I noticed that the first version takes up more than 10 MB of RAM all the time it is running. The second version takes up far less RAM - less than 1 MB of RAM most of the time and 2 or 3 MB of RAM during the short times that the embedded AppleScript runs (every 10 seconds). Neither version takes significant amounts of CPU - the first version averages about 0.2 % of the CPU on my iBook 1.2 GHz G4. The second version takes too small a percentage of CPU to measure. Of course you would only use one of these two versions at a time. Here's the VersionA script: Code:
-- This AppleScript displays the amount of memory being used by SafariHere's the VersionP script: Code:
#!/usr/bin/perlI'm interested in hearing what others think of this script (either version) since I will probably submit this as a "hint" on the main macosxhints site and it is best to get the bugs out ahead of time. Of course any suggestions for improvement (either of functionality or implementation) are welcome. |
A simple copy paste of the first code to Applescript doesn't seem to accomplish anything - no errors and no response.
The perl script gives the following output when attempting to run: 2006-12-14 10:16:30.034 osascript[576] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:16:30.052 osascript[576] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:16:40.296 osascript[579] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:16:40.313 osascript[579] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:16:50.554 osascript[582] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:16:50.569 osascript[582] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:17:00.841 osascript[586] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX 2006-12-14 10:17:00.859 osascript[586] CFLog (21): dyld returns 2 when trying to load /Users/mjw1/Library/ScriptingAdditions/24U Appearance OSAX.osax/Contents/MacOS/24U Appearance OSAX I canceled the process after these many popped up. Is that by design? |
The first script fails on mm with the message: "Result of numeric operation was too large" - I think because of a divide by zero somewhere, possibly.
As I go through the handlers, on mm the first: "getProcessMB" produces a null result. The AppleScript below works for me in its place (I haven't examined the others yet): to getProcMB(procName) -- as string try return ((last word of (do shell script "/bin/ps -xc -o command,rss | grep " & procName)) as integer) / 1024 -- result of a division will be real on error display dialog "The targeted process is not running" end try end getProcMB getProcMB("Safari") |
Quote:
Quote:
I've not had it crash on me - yet. Will run it for a while. |
With respect to appIsRunning, I would have done this:
on isRunning(appName) tell application "System Events" to if exists process appName then return true return false end isRunning isRunning("Safari") |
My final version (which runs on my machine)
Code:
on showAppSizeInTitle(appName, warnMB) |
And mine is ...
Code:
-- This AppleScript displays the amount of memory being used by SafariNote script crashes if you accidentally close all windows - so I've added in a 'count the windows first' check above. Interesting that when started here (without a homepage) Safari uses 48.6 MB, and takes up more memory thereafter. Resetting the cache drops this down. So Safari is storing at least some component of the cache in RAM - so is Apple doing this for performance reasons or because it's a bug? One could have the script auto-quit the app and then re-launch it. Or clear the cache - there is a keyboard shortcut that could be called from Applescript - although this means clearing the whole cache, and may not be ideal. |
You can avoid the crash by substituting this for my isRunning:
Code:
on isRunningHasWindow(appName) |
Quote:
I (obviously) don't see this in my tests. I don't see anywhere that I could be dividing by zero. Quote:
Maybe you could try the following stand-alone Perl script to see if you get similar problems. Sample usage: getProcessRss Safari Code:
#!/usr/bin/perlQuote:
There is usually only one app running whose name includes the string "Safari" so it will usually work in this case. But it will fail if you tried it with "iTunes" since there is also an "iTunes Helper" app running. |
Quote:
|
Here's a revised version of the "VersionA" script (the AppleScript application) that integrates the improvements of NovaScotian and bramley:
Code:
-- This AppleScript displays the amount of memory being used by SafariBut I haven't tested that this alert works if there are no windows - I'm just assuming it does. By the way, I kept the 'appIsRunning' subroutine in the more verbose form since it is easier to generalize it starting from that form. E.g. if I wanted later to add another condition on the app. Another note: I kept the 'removeLastPart' subroutine using: set str to str's text 1 thru text item -2 instead of the simpler: set str to str's text 1 since that way it is more robust in the (hopefully unusual) case where the delimiter (" *** ") happens to occur in the original title. I'm not sure why, but I now am finding that this AppleScript application is taking about 5 MB of RAM whereas before it was taking more than 10 MB. I'm not sure what caused this difference. By the way, if I change the script to use the following in place of the 'on idle' handler, I find that it takes about twice as much CPU. It still is not a significant amount of CPU, but it seems to be averaging about 0.4 % CPU with the 'repeat' loop compared with about 0.2 % with the 'on idle'. Code:
repeat |
I've tried the new version, Hayne, (and the perl script itself) and neither of them return a result for me.
In my /usr/bin/, I find perl and perl5.8.6. Can that be confusing the issue? I've never run either of them before. |
Quote:
What model of Mac and what version of OS X do you have? Could you please run the following command (in a Terminal window) and then show us the results (I'm assuming that you have Safari running): /bin/ps -xc -o command,rss | grep Safari | vis -w |
Quote:
where there was clearly no return after the instruction since ACB-G5:~bellac is my normal prompt. My machine/system are in the signature (you must turn them off): dual-core G5/2.3 running Mac OS X 10.4.8 |
Quote:
Could you please run the following modified version of the stand-alone Perl script and show us what you get from: getProcessRss Safari Code:
#!/usr/bin/perl |
I'm the wrong guy to do this Hayne - I'm convinced now that my problem is that I'm doing something wrong in Terminal to get your file running. If you want me to run it you'll have to supply the command line to do it. I've become totally confused and don't even know whether I've rendered your perl script executable properly, and certainly don't know how to run a perl script. perl is usually silent on all the variations I've tried.
Sorry :confused: |
NovaScotian:
1. In your browser, select the perl script above, hit command-C to copy it to the clipboard. 2. In your Terminal, type sudo pico /usr/local/bin/getProcessRss (this assumes that you have a /usr/local/bin directory. If you don't, you should--create one with sudo mkdir /usr/local/bin and add it to your PATH in your shell configuration file, such as ~/.bash_profile or ~/.bashrc) 3. A simple text editor, pico, will open in your Terminal. Now, hit command-V to paste the script in your clipboard into pico. Now, hit control-X to save, and hit the Y key to confirm, then hit Return. (Note that you are using the Mac's clipboard when pasting, so you use the command key. When you are in pico, you are using a Unix app, so you use the control key. 4. Enter the following command to make the script executable: sudo chmod 555 /usr/local/bin/getProcessRss 5. Test it as hayne requests. Trevor |
Quote:
Open that file in TextWrangler (or some other text editor that can tell you about the line endings) and click (once) on the toolbar icon at the top that looks like a little page. This will bring down a menu where the first 3 menu items are: Macintosh, Unix, DOS. You want it to be Unix. Save the file if you had to change this. Now, go into a Terminal window and navigate to the folder where you saved that script. (See the "navigation" section of this Unix FAQ if you need help with this) Then type in (or copy & paste from here) the commands: chmod +x getProcessRss ./getProcessRss Safari where I have assumed that you saved the script in a file named "getProcessRss". The first of these commands ('chmod') makes the script executable. The second command runs the script with a command-line argument of "Safari". |
Thank you both, gentlemen. The first step on Hayne's agenda was an unknown to me. I use BBEdit, but had never noticed that I had a choice of line endings - in AppleScript that's looked after for you.
I altered your processes slightly, but the result was: ACB-G5:~/Desktop/Hayne bellac$ ./getProcessRss Safari Looking for: "Safari" PS: "Safari 42460 " 41.5 MB I found it easier (because editing typing in the Terminal is so unnatural for me) to type chmod +x and drag the file icon to the terminal and return (I use iTerm). Then cd and drag the folder it's in to the Terminal, return again Then ./getProcessRss Safari -- after opening Safari, but it works equally well for Camino. |
Just got to This.
The Latest script runs fine, with Safari running or not. My Safari seems to be running at around 25.X MB. But the VersionA runs at 14.x MB. |
Quote:
In general, Unix utilities (e.g. Perl) will assume that all text files have the one-true-line-ending ('\n' or "Unix-style) and will silently fail if this is not the case. This is true of the script files (like this Perl script) and the data files they are processing. See the "line endings" section of this Unix FAQ for more info on this. I suspect that the problem with the Perl script that is embedded within the AppleScript (in the subroutine 'getProcessMB') not working is due to the same sort of problem. I worried a bit about this when I wrote that AppleScript - I wasn't sure that the line-endings on that embedded Perl code were going to be correct. But I typed it into Script Editor and then wrote the embedded script to a file and it all checked out fine. But I just read today on the AppleScript users mailing list that "Script Editor" and Satimage's "Smile" use different line endings - Smile uses the traditional Macintosh line endings. And I now realize that this might be relevant here. I'm guessing that maybe NovaScotian used Smile to save the AppleScript and that is why it isn't working for him. It's a bit nasty having to rely on the behaviour of one editor (Script Editor) to get the embedded line endings correct, but the only alternative I can see is to add in explicit newlines in the embedded Perl code and then string it all together into one unreadable mess of one line in the AppleScript. I thought it was bad enough that I had to escape all the backslashes and quotes in that embedded Perl! Bottom line: for the moment, NovaScotian, could you please try copy & pasting the above AppleScript into "Script Editor" instead (assuming that you were using "Smile" before). |
Quote:
That's the sort of thing I was seeing last night. But now it is taking much less as I mentioned above - I don't understand why there would be this difference. |
Quote:
Will Run VersionB shortley |
Quote:
VersionA (A for AppleScript) and VersionP (P for Perl) :) |
Doh.. :rolleyes: its late here.. or I should say early (gone 6 in the morning). Have not been bed yet.
I also find that when safari is playing up it uses lots of cpu, this sometimes happens after I have gone into another app just after safari, and at some point things get slow, a lot of beach balling. When I look for the reason why, A lot of the time its Safari in the background. I do not know how helpful this is but I added cpu to the script. It rough but seems to work. I put 50% cpu warning but I may lower it when I get a gauge on it. Code:
on showAppSizeInTitle(appName, warnMB)Url,time, usage |
Neither of Hayne's versions work for me, though the perl script above by itself does.
I'm running this using a hot key rather than a idle script: set tApp to "Safari" -- name of browser set tLim to 75 -- acceptable memory use in MB memToTitle(tApp, tLim) -- get title to include memory use (* Script concept by Cameron Hayne, version by Adam Bell *) to memToTitle(appName, tooHigh) tell application "System Events" to if exists process appName then -- it's running try tell process appName to set N to name of window 1 set OK to true -- it's running and has a window open on error set OK to false -- no window end try else set OK to false -- not running end if -- Running with window; get memory estimate to four significant figures if OK then set MB to (100 * ((last word of (do shell script "/bin/ps -xc -o command,rss | grep " & appName)) / 1024) as integer) / 100 -- Put estimate in window title tell application appName -- remove previous addition first set dlim to " @ Mem = " -- something not likely to be found in a title set tid to AppleScript's text item delimiters set AppleScript's text item delimiters to dlim if (count N's text items) > 1 then set N to N's text item 1 set AppleScript's text item delimiters to tid set name of window 1 to N & dlim & MB & " MB" if MB > tooHigh then display alert appName & " is consuming more than " & tooHigh & " MB of memory" end tell end memToTitle Note: Unlike Hayne's version, this one doe not trigger a warning if there are no windows. This works in Camino as well. |
Quote:
|
I had not, Hayne, but just did (I've been out all day). I'm using Script Debugger 4 which may be part of the problem, because in Script Editor, the script with the command below succeeds every time I run it.
Problem solved, apparently. Excellent sleuthing, Hayne. Code:
--on idle |
Last Minute Addition to previous post:
I copied the whole script from Script Editor to a new document in Script Debugger 4 and it works. - Obviously the line ending problem arises from copying from the web page, rather than having the script as an original. I have found that it also works quite nicely in Camino, but getProcessMB fails when the app is Firefox, and Shiira doesn't know about its windows. It works beautifully in NetNewsWire (which does have a memory leak and grows very slowly if left running). I may modify it slightly to watch both. Sorry to be so troublesome. |
Quote:
This is an interesting issue for any embedded script (for use with 'do shell script') in AppleScript - a much more general issue than just the particular script under discussion in this thread. It is also an issue if some embedded text in an AppleScript were to be written to a file (using 'write' in AppleScript). See for example, the following AppleScript: Code:
-- This AppleScript is intended for testing the behaviour of If I comment out the line that invokes the 'fixLineEndings' subroutine, and then run the above AppleScript from "Script Editor", it still reports that the file is "ASCII English text". But when I run the identical AppleScript (with the call to 'fixLineEndings' commented out) from "Smile", it reports that the file is "ASCII English text, with CR line terminators". |
Quote:
Quote:
(The full path to the executable is /Applications/Firefox.app/Contents/MacOS/firefox-bin ) |
Quote:
I hope you'll report back to this thread if you figure out how to deal with script that have required line endings. One way (that I haven't tried yet) would be to replace all the \r with \n in a first line of the script and another would be do do that using text item delimiters and ASCII character 10. Finally, if the server of a site happens to be a Windows machine, then it can have CRLF as a paragraph delimiter, and that's often a problem with copied material. A generic way to fix this would be great, and I see that you've got "feelers" out for that. Please let us know if you find a "best" way. |
Quote:
With the use of this subroutine, that AppleScript gives the same result whether compiled in "Script Editor" or in "Smile". If I comment out that subroutine, the same script gives different results when using "Script Editor" than when using "Smile". I'd be very interested to hear what happens if you use "Script Debugger". I think (but I haven't tested it) that the above 'fixLineEndings' subroutine might work to fix any string - even one with Windows-type line endings. |
Quote:
Code:
% getProcessRss firefox-bin |
Quote:
But even that does not work. The sdef for Firefox is very very basic. It does not even have the Apple Script basics. That could be why? |
Quote:
Good stuff, CH. PS: your fixLineEndings is the simplest way to do it too. |
For the shell-script impaired, the test for line endings can be done in AppleScript too.
set txt to read alias ((path to desktop folder as text) & "SomeText.txt") whichEnding(txt) on whichEnding(T) set NP to count paragraphs of T -- produces the same number for any ending set tid to AppleScript's text item delimiters set AppleScript's text item delimiters to (ASCII character 13) & (ASCII character 10) if (count text items of T) = NP then set AppleScript's text item delimiters to tid return "Windows" end if set AppleScript's text item delimiters to ASCII character 13 if (count text items of T) = NP then set AppleScript's text item delimiters to tid return "Mac" end if set AppleScript's text item delimiters to ASCII character 10 if (count text items of T) = NP then set AppleScript's text item delimiters to tid return "Unix" end if set AppleScript's text item delimiters to tid end whichEnding |
Quote:
It seems to be returning before resetting "AppleScript's text item delimiters" to what it was. And hence the delimiter value will affect subsequent AppleScript in that script and all other until the environment is restarted (e.g.Script Editor is re-launched). |
Good catch, Hayne;
Several things wrong with it and that was only one of them. Order is important too. I've corrected the original rather than leave it stand broken. |
Quote:
Your subroutine doesn't return anything in this case ('NP' would not match any of the "pure" cases). This subject is tricky. |
Here's an AppleScript convertor to fix line ends in a saved document.
Code:
set tDoc to (choose file without invisibles) |
Further question:
Why doesn't something like this work? set foo to (read (choose file without invisibles)) set the clipboard to (do shell script "echo " & foo & " | tr \\r \\n") |
Quote:
Code:
set foo to (read (choose file without invisibles)) |
Thanks Mark, that does solve the problem of getting the shell script to work, but the result seems to be converted back by the system because when I check, the endings are still returns.
PS: I've been informed on another list, for the benefit of those who use Script Debugger 4 that there is a File menu pick to save with Unix endings. Would have saved a lot of grief. |
If I run your script from Post #41 on a plain text file, I get "Mac".
If I combine the two scripts I get "unix" Code:
set foo to (read (choose file without invisibles)) |
Quote:
From http://developer.apple.com/technotes/tn2002/tn2065.html Quote:
|
Quote:
|
Here's a new version of the above AppleScript for displaying Safari's memory usage. This version has the warnings (for exceeding the warn-level of memory usage) delayed longer each time you okay the warning dialog. This is useful in situations where Safari is exceeding the warning-level but you don't want to quit it immediately.
This version also has a property 'useDelayLoop' that you can set to make it possible to do test runs of the script from Script Editor. Code:
-- This AppleScript displays the amount of memory being used by Safari |
Runs nicely for me from either the Script Editor or Script Debugger 4. The fixLineEndings does it - bombproof!
|
New & improved! :)
Here's an improved version of the script. This version implements 'mark hunte's idea of showing CPU usage as well as memory usage. It shows Safari's CPU usage (in the title of the Safari window) whenever it exceeds 10% (I don't think having it give a warning dialog upon exceeding a certain CPU usage would be good since this script is intended to be left running all of the time, and sometimes you would expect Safari to be using a lot of CPU - e.g. when playing a Flash or Java game) This version might also be slightly more efficient (use less CPU) since it gets the process-id of the Safari process from System Events and then runs the 'ps' command only for that one process instead of getting info on all processes from 'ps' and filtering out all the rest. Code:
-- safariInfoInTitle: |
Works very nicely on MM, Hayne. With useDelayLoop set to true and the first handler set as:
Code:
on update() |
Thanks for testing this, NovaScotian.
By the way, if I add a line: updateAppInfo("firefox-bin", 150) then it does work fine as far as giving warnings about Firefox's memory usage (if it exceeds 150 MB with the above line). But as discussed in an earlier post, it doesn't show the memory usage in the title since Firefox is not AppleScriptable. |
There's a "hack" that makes it possible to get/set the name of a Foxfire window from Rob Griffiths in MacWorld.com. I haven't tried it for Foxfire (which I don't like) and don't particularly recommend it - I just point it out. I've used it successfully with Preview because it is useful to be able to manipulate its windows.
|
Thanks for reminding me about that hack to enable basic AppleScript support in apps that don't by default support AppleScript.
I haven't tried it yet with Firefox |
Here's a new version of the script with the following changes:
Here's the script: Code:
-- appInfoInTitle: |
Hayne,
Just like to say this app is very useful, Thanks |
Quote:
I still find it strange that sometimes this AppleScript application seems to take 10 or 15 MB of RAM and then if I quit it and re-launch it, its RAM usage is down to 5 MB. I'd be interested to hear if others are seeing this phenomenon. By the way, I edited the above (latest) version of the script to make it round the memory & CPU numbers to integers. It seemed silly to be reporting 1/10ths of MBs - and this saves a bit of space on the title-bar as well. |
My copy never seems to get below 14mb on my tower and PB.
I suppose I should be happy with it being consistent? :) thanks again. Mark |
[ot]
Speaking of "useful", Daniel Jalkut's Blog has some very flattering remarks, Hayne, for ash as a means of running AppleScripts via ssh when he's away from his machine. Looks neat. :)
|
Quote:
At one point I thought maybe it was somehow correlated with whether you had the script open in Script Editor or not. |
I have edited the above (latest) version of the 'appInfoInTitle' script to add an error check on the result from 'getProcessInfo'.
And I'd encourage any users of this script to install Growl (http://growl.info/) and enable the 'appInfoInTitle' script to use Growl for warnings by setting the 'useGrowl' variable to true. Growl is very elegant. |
Works beautifully with Camino and NetNewsWire added to the mix.
|
Quote:
15190 Safram4g mark 0.60 1 14.73 MB 143.84 MB |
Quote:
Have you tried quitting Script Editor and then running the script? I never figured out what caused it to sometimes take more RAM sometimes but on my machine it now seems to be more consistently taking 5 MB. And check that you have it compiled as an application bundle. |
Ok tried again.
I did not have it as a Bundle. It seems to take at least two new launches after quitting Script Editor to get it down from 14.. to 5.. I do sometimes get high Ram if I get a warning first thing after launch? will try it with growl. If I bring SE back up after launch then all is ok. |
I have just go consistant results doing the below.
Open the Script (RAM app) in SE, Leave its window open and then launch the RAM app (above 14mb) Open the Script (RAM app) in SE, Edited it and Save it, Leave its window open and then launch RAM app (above 14mb) Open the Script (RAM app)in SE, Edited it and Save it, close its window. Leaving SE running, and then launch RAM app. (above 14mb) Open the Script (RAM app) in SE, Edited it and Save it, close its window. Select a second script window and do a Save in that, leaving it open and then launch RAM app I get low ram. (under 4mb) |
Hi, Hayne,
I noticed from the Hint you posted today that Some people had an issue with Growl, either they did not want it or they did not read the instruction correctly. and ran in to compiling issues. So I changed the script so if you do not have growl then the the Growl property will be ignored when the script is run. Also you will not be asked for growl when you first compile, as I have used a run script to call the growl app. Code:
-- appInfoInTitle: |
Mark:
Thanks for the idea of using a 'run script' to avoid compile-time issues with AppleScripts referring to apps that might not be installed. I'll probably integrate this into my version of the script soon. |
| All times are GMT -5. The time now is 01:42 PM. |
Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.