July 28, 2025·4 min read·engineeringcglibcmallocruntime

glibc 2.42 lets malloc cache larger allocations per thread

Larger tcache entries are opt-in in glibc 2.42. The release also expands C23 mathematics and adds an explicit executable-stack compatibility setting.

glibc 2.42 extends an existing allocator policy to a larger class of requests. Its thread-local allocation cache can now handle large blocks when an application explicitly raises glibc.malloc.tcache_max. The July 28 release announcement gives a maximum setting of 4,194,304 bytes. Upgrading the library alone does not enable that larger cache range.

That opt-in matters because an allocator cache changes where freed storage remains available for reuse. The unit of reuse here is a thread, which makes the lifetime and distribution of allocations relevant. A statement that the release supports larger cached blocks is therefore useful. A statement that every application will become faster from enabling the maximum would go beyond what the announcement establishes.

I tend to judge C runtime changes through the boundaries they affect. A new allocation policy can alter behavior without changing a function signature. The useful question is which allocations follow a different path and what configuration activates it. This release makes that choice available through the existing tunable interface.

The allocator adds large size classes

The allocator source at the glibc 2.42 tag defines 64 small tcache bins and adds 12 large bins. Its tunable-setting code converts a requested size into the allocator's internal chunk size, then uses the large-bin indexing path when that size exceeds the small-cache range. It checks that the resulting index remains within the available bins.

Those details qualify the public setting. The user supplies a request-size threshold, while the allocator works with chunk sizes that include its layout and alignment requirements. Accepting the setting includes checking the converted size. The release's maximum is a documented configuration bound, not a guarantee that every conceivable request of that size follows an identical internal allocation path. This also explains why the implementation distinguishes the externally supplied threshold from its internal bin index instead of treating the number as an array position.

For a program normally invoked as ./application, opting into a larger range can look like this. The value is an example below the announced maximum, not a workload-specific recommendation.

env GLIBC_TUNABLES=glibc.malloc.tcache_max=65536 ./application

The operational tradeoff follows from per-thread retention. Storage retained for reuse by one thread is not the same as storage immediately returned to the operating system. Larger eligible blocks increase the amount of memory that a cache can retain for its work. Whether that is a useful exchange depends on allocation reuse and the number of participating threads.

The source also retains a per-bin count limit, with a default of seven chunks. Size eligibility and the number of cached chunks are therefore separate aspects of the policy. Increasing the maximum eligible request does not mean the cache has no bound, and it does not establish a single fixed memory cost for every process.

There is a documentation detail worth preserving. The tcache section in the tagged tunables manual still describes the older small-size maximum. For the new capability, the release announcement and tagged allocator implementation agree on the expansion. Reading versioned documentation is useful, but checking it against the implementation can reveal a section that has not caught up with the code it describes.

Compatibility for executable-stack modules becomes explicit

glibc 2.41 stopped dlopen and dlmopen from making a non-executable stack executable to accommodate a newly loaded library. The 2.42 release adds a compatibility setting for applications that depend on such modules. Its mechanism is to make the stack executable at process startup.

The 2.42 tunables manual gives glibc.rtld.execstack a value of two for that purpose. Zero disables executable-stack negotiation, while one permits the normal startup negotiation. Two forces an executable stack from the beginning. It changes the process's initial state so a later module does not require the loader to change it.

That distinction preserves the explanation of the earlier restriction. The late load is not granted a new ability to modify a non-executable stack. The compatibility mode arranges an executable stack in advance. It is consequently a process-wide permission choice, with a different scope from editing the metadata of one library.

I prefer the explicitness of that mechanism to an invisible permission change during an extension load. An application that needs the compatibility setting can name the requirement in its launch configuration. The consequence remains concrete, because the setting forces an executable stack even before the module that requires it loads.

C23 mathematics continues to reach the library

The release adds the C23 function families compoundn, pown, powr, rootn, and rsqrt. The announcement traces them to TS 18661-4:2015 and lists variants for the supported floating types, with type-generic macros in tgmath.h. These are runtime-library additions that complement compiler work on the language revision.

glibc also adds the draft C2y unsigned absolute-value family, including uabs and uimaxabs. Their draft status should accompany their names. Availability in this library release does not mean they were already part of C23, or that another C library exposes them under the same feature-selection policy.

Several pi-scaled single-precision functions gain implementations from CORE-MATH. The announcement describes those implementations as optimized and correctly rounded. That is a specific numerical property of the imported work. It does not establish a measured speedup for an arbitrary program that happens to link the release.

These changes affect applications through different mechanisms. New function calls introduce library requirements. The larger allocation cache changes an existing implementation policy when configured. Executable-stack compatibility changes startup permissions. A deployment may need one of those changes without benefiting from the others, so the release number alone is too broad a description of the reason to adopt it.

For the allocator change, the concrete adoption boundary remains the tcache threshold. A program can upgrade to glibc 2.42 while retaining its prior eligible size range. Raising that threshold deliberately makes larger allocations candidates for per-thread caching, with the retention and reuse behavior that choice entails.