Memory Issue when repeating a "Type a Keystroke" command

I don't need to simulate a cmd+L.

(Let's use "a" since "l" looks like "I").

Here's what I need to do:

  • Press a key combo - cmd+shift+a
  • Start a loop that presses the "a" key every .1 seconds until
  • I press the cmd key, which should cancel the loop

Example:

Functionally, it works exactly as expected. However, I ran it for 60 seconds, typing a single letter over and over into sublime text editor without the delay (and no other macros in between) and this is what I see:

Start Memory: 22.5 MB private / 44.9 MB shared
End Memory: 31.5 MB private / 331 MB shared

start memory

end memory

OK, but isn't that what your While loop is doing? You're holding down the Command key and typing a letter (and waiting a tenth of second after).

When what you want to do, it seems, is simply continually send a letter and then wait a tenth of a second.

I'd purge the modifier as a test for the loop.

The while loop is continually pressing "a" until I press the cmd key (which then breaks the loop).

Ah, missed that. Sorry.

I would, though, still lose the modifier and try some other test. It's a modifier after all.

At mrpasini request, removed the conditional while. Updated macro to simply loop 4000 times. Removed the .1 delay to speed things up.

Restarted KM engine. Ran the macro. Took about 40 seconds to complete.

Start Memory: 23.1 MB private / 42.7 MB shared
End Memory: 23.9 MB private / 194.8 MB shared

start loop

end loop

And a screenshot of the text editor showing that no keypresses were missed or timed out:

I should have mentioned this earlier, but take a look at the Keyboard Maestro log for a clue.

Nothing in the logs. I tail them regularly when working with KM. Here's an except from earlier today:

2022-07-25 10:16:22 Execute macro “Loop Test” from trigger The Hot Key ⇧⌘A is pressed
2022-07-25 10:16:22 Log: ----- Start Loop -----
2022-07-25 10:16:47 Log: ----- End Loop -----

Did you walk away and came back later?

I was playing with this yesterday and, for me at least, I could run your original macro for a couple of minutes, end it by pressing the ⌘ key, and characters continued to be typed into Script Editor for ages. That suggests to me that KM is buffering simulated keypresses and managing their submission to the event queue -- the memory bloat is possibly the buffering.

The loop version from here (Memory Issue when repeating a "Type a Keystroke" command - #16 by ericv) runs in about 40 seconds and didn't cause any buffering or UI issues in Sublime text editor.

But as noted here (Memory Issue when repeating a "Type a Keystroke" command - #9 by ericv) even if I set the delay to 1 second (which is huge for a simple keypress macro), if I walk away it continues to build up the shared memory usage until the system becomes unresponsive.

Appreciate you trying it! Did ending the macro and letting the buffer finish free up the memory? In my experience, the only way to free that shared memory is to restart KM Engine. I have left it for hours hoping for some trash collection or something to grab it, but it never cleans itself up.

Yeah, I've just plonked BBEdit onto my test machine and that reacts a lot more quickly than Script Editor, with no visible buffering. Edit to add: That was on a short test -- on a longer run -- 100,000+ characters -- or, perhaps, as memory use climbs, buffering becomes apparent.

That doesn't mean it isn't buffering, though! IIRC the system's Events buffer is pretty limited and so the Script Editor test suggests there's buffering in KM (which would explain the climb in memory usage). 150-200 keystroke events per second is somewhat beyond "emulating normal user interaction", so I'm not surprised we don't usually notice it!

My (very basic!) understanding of "modern" memory management is that apps don't need to "free" unused memory until they exit or the OS grabs it back for use elsewhere. So my hope was that running the macro repeatedly would keep re-using that extra buffer space. Unfortunately not -- it keeps adding to it.

Hmmm... 1GB of memory use is ~65,000 characters, ie 2^16 -- that's a very computery number and, perhaps, a clue...

I've got to ask again -- how many keypresses do you actually need? While this is a fun exercise, and I'm happy to keep poking, my 8GB Air has managed to type 500,000+ ls into BBEdit with no sign of stopping... That's an excessive amount of keypressing for even the worst no-lifer game routine!

This is exactly why I only noticed it recently. I've been using KM for years and always blamed it on the game or the system not being restarted (which are probably issues as well - but...)

The keypresses actually come in groups. Usually a bunch of them for a limited amount of time. "Do this thing for 10 minutes while I run to the restroom." or "Click this thing for 20 minutes while I grab some food."

My expectation was that, even if the memory went up, when I stopped the macro it would free it and then I could go again. It wasn't until I started digging deeper that I realized it was KM.

The main thing that makes me go :thinking: is that using the Applescript, while slower, doesn't seem to cause the shared memory increase. Maybe it is just moving that shared memory issue to another place that I am not seeing it, but it seemed odd.

I will look at this again, but as referenced in the other thread, there does not appear to be a memory leak in Keyboard Maestro per se, but memory used by the system increases.

AppleScript might behave differently for any number of reasons - for one thing that is Apple’s code doing the work, and they could use any number of different, private APIs for the purpose.

As far as the connection failing issue, AppleScript is run through osascript, which is executed in a subprocess, and then likely requires some for of inter-application communication as well. Anything that executes large numbers of subprocesses in quick succession, or connections in quick succession, is likely to run out of some sort of needed resource that is periodically cleaned up by the system (such as process/connection ids or ports, process/connection table space, etc). This is likely what is happening.

You might like to try instead running a single AppleScript that continuously sends the keystrokes. Then have it check whether to continue. Possibly use the existence of a temporary file (eg "/tmp/stopnow") as a single to stop so there is no direct inter-application communication required.

Interesting. I'll give that a try. I may also try stuffing the results into a variable and if it's not a success, try again, since it seems to recover pretty quickly. :thinking:

I was hoping to have another data point for you, @ericv, but my laptop's still processing "queued" keypresses even though the macro stopped a day ago...

BBEdit's document window has recorded 1.2 million characters so far, KME is reported as using 32GB of memory (on an 8GB machine), and nothing's crashed. You said in your OP that "the entire system hangs" -- I'm wondering if it actually has hung or if it is unresponsive because of processing the "queued" events.

I doubt you'll be sending such an extreme number of keypresses during a 20 minute break to grab some food so, if @peternlewis's "external" AppleScript solution doesn't help, perhaps you can get away with an action at the end of your keypress macro that flushes the memory by quitting and relaunching KME. I'm thinking an async AppleScript that checks KME memory allocation and if that's over eg 1GB waits until no macros are running and then quits and opens the Engine app.

1 Like

Can you share the macro you used for this? Interested why your KM Engine isn't bloating shared memory.

Oh, but it is -- that's around 450GB reported! And still not crashing... But shared climbs proportional to the KME allocation reported in Activity Monitor or top, and those are much easier numbers to get for a scripted "shall will kill it yet?", which is why I mentioned it.

The macro is the same as your first posting, minus the logging and pause actions.