Subject: Re: bug#79023: 30.1.90; Suspicion of memory leak on internal_redisplay (MacOS)

From: Przemysław Alexander Kamiński<[email-masked]>
Date: Wed, 16 Jul 2025 16:15:54 +0200

> Sorry, I don't understand what that means.  Are you saying that you
> load many packages at startup?  How many?
>
> Could we please start the measurements from "emacs -Q", to avoid
> potential influence of too many unknowns?

Probably around 100-200, and it's mostly popular setup: LSP/Vertico+friends some theme and fonts.
This shouldn't be 1Gb at start right?

>
> I've just left "emacs -Q" running for 10 minutes, after setting
> blink-cursor-blinks to zero (so it keeps blinking indefinitely), and
> not only did it not grow in memory, it _freed_ some memory.

Are you on MacOS? Because that patch that I made fixed the issue for me as well (the one with autorelease).
If you are than it would mean that issue might be deeper and be caused by gcc installation... that'd be fun..

> It's possible that on macOS there's some leak, but we need a much more
> specific information to find where it happens.  Just looking at the
> memory allocation calls will never tell us anything useful, because
> Emacs calls malloc _a_lot_.

I know, trying to make it happen. IMO "emacs -Q" won't help because if leak happens on local object allocation or event handling there won't be outliers.
I can confirm at least one thing though - if window isn't drawn, Instruments doesn't report any leaks.

>> I've noticed that on built Emacs there's allocation for every single blink of cursor on screen. Isn't this weird even if that wouldn't leak.
>
> No, it isn't weird at all.  (...) All these actions allocate memory (which is
> then freed, some of it immediately and some later, via GC).
>
I'm spoiled by my recent work with Zig because and would expect that there's pool or arena, I'll look into this when I can shuffle the code instead of rebuilding it from scratch.

>> I've done more testing today with different build flags and some guesses that I have:
>> - Some allocated strings aren't marked as autorelease, so they stay
>
> What do you mean by "marked autorelease"?  Lisp strings are released
> by GC when they are no longer referenced from any other object.
> There's no such thing as "autorelease strings" in Emacs.

Not in Emacs, but it's in Cocoa. As far as I know it's using ARC. Some NSStrings are allocated in integration module.
Since this allocation is outside of normal malloc I wouldn't expect it to be GC by Emacs (still on my theory that the bug sits in integration code).

I'm not a Objective-C developer, but some NSStrings are marked with "autorelease" upon creation and some are not - I'm suspicious.
Could it be the cause if that'd be single glyph that can't be released...

>> - For some reason system doesn't know that this allocated space is no longer used and doesn't GC it
> How do you see that?  And which space is it, allocated by what
> functions?

After 4 hours of usage I have 2Gb of memory kept. Garbage collection doesn't touch it at all.
I don't know what exactly because unfortunatelly I didn't run Emacs long enough for it to gather data.

>> - Slowness is caused by immense fragmentation of memory caused by small allocations far away from each other
>
> Emacs relies on the system malloc to be able to avoid fragmentation.
> Where that is not guaranteed (such as on MS-Windows), Emacs has its
> own replacements for the system malloc, which avoid fragmentation.

I'm wondering if the build system that _most_ MacOS builds are using aren't at fault and use gcc's one.
It would make sense - on Windows Emacs feels snappier than on MacOS..

>> - During deallocation hashes aren't deallocated completely
> Which hashes are those?

I was looking at

hash_table_alloc_bytes (ptrdiff_t nbytes) ATTRIBUTE_MALLOC_SIZE ((1));
hash_table_free_bytes (void *p, ptrdiff_t nbytes);

There seems to be some calculation during release, maybe something goes wrong?

>> - There's missing some MacOS specific call that should be made in order for it to optimize
> Which calls, and where are they missing?

Probably Alan (hi!) could help; I have no idea. But I know what happens when you don't type "sync<RET>" before you pull out the floppy ;-)

Best,
Przemysław Alexander Kamiński