tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 4f2ba149596907cbee438a1ef91b8ec9dfc8813b
parent 8bceaea4ea5ce031b0932e7665b9c39d728d15ee
Author: Paul Bone <pbone@mozilla.com>
Date:   Fri, 17 Oct 2025 03:53:25 +0000

Bug 1980048 - Remove the tiny size class from mozjemalloc r=glandium

Differential Revision: https://phabricator.services.mozilla.com/D259218

Diffstat:
Mjs/src/jit-test/tests/heap-analysis/byteSize-of-string.js | 18+++++-------------
Mmemory/build/Constants.h | 24++++++++----------------
Mmemory/build/Globals.h | 5++---
Mmemory/build/mozjemalloc.cpp | 44++++++++++++++++----------------------------
Mmemory/build/test/gtest/TestPHC.cpp | 14++------------
5 files changed, 33 insertions(+), 72 deletions(-)

diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js @@ -332,19 +332,11 @@ checkEq(byteSize(ext16), s(RN, RN) // allocated in the nursery. If this ever changes, please add tests for the new // cases. Also note that on Windows mozmalloc's smallest allocation size is // two words compared to one word on other platforms. -if (getBuildConfiguration("windows")) { - checkEq(byteSize(newString("", {external: true})), s(EN+8, EN+16)); - checkEq(byteSize(newString("1", {external: true})), s(EN+8, EN+16)); - checkEq(byteSize(newString("12", {external: true})), s(EN+8, EN+16)); - checkEq(byteSize(newString("123", {external: true})), s(EN+8, EN+16)); - checkEq(byteSize(newString("1234", {external: true})), s(EN+8, EN+16)); -} else { - checkEq(byteSize(newString("", {external: true})), s(EN+4, EN+8)); - checkEq(byteSize(newString("1", {external: true})), s(EN+4, EN+8)); - checkEq(byteSize(newString("12", {external: true})), s(EN+4, EN+8)); - checkEq(byteSize(newString("123", {external: true})), s(EN+8, EN+8)); - checkEq(byteSize(newString("1234", {external: true})), s(EN+8, EN+8)); -} +checkEq(byteSize(newString("", {external: true})), s(EN+16, EN+16)); +checkEq(byteSize(newString("1", {external: true})), s(EN+16, EN+16)); +checkEq(byteSize(newString("12", {external: true})), s(EN+16, EN+16)); +checkEq(byteSize(newString("123", {external: true})), s(EN+16, EN+16)); +checkEq(byteSize(newString("1234", {external: true})), s(EN+16, EN+16)); checkEq(byteSize(newString("12345", {external: true})), s(EN+16, EN+16)); checkEq(byteSize(newString("123456789.123456789.1234", {external: true})), s(EN+48, EN+48)); checkEq(byteSize(newString("123456789.123456789.12345", {external: true})), s(EN+64, EN+64)); diff --git a/memory/build/Constants.h b/memory/build/Constants.h @@ -57,23 +57,19 @@ // minimums and how memory is allocated in each range the maximums can be // calculated. -// Smallest size class to support. On Windows the smallest allocation size -// must be 8 bytes on 32-bit, 16 bytes on 64-bit. On Linux and Mac, even -// malloc(1) must reserve a word's worth of memory (see Mozilla bug 691003). -#ifdef XP_WIN -static constexpr size_t kMinTinyClass = sizeof(void*) * 2; -#else -static constexpr size_t kMinTinyClass = sizeof(void*); -#endif - -// Maximum tiny size class. -static constexpr size_t kMaxTinyClass = 8; +// On Windows the smallest allocation size must be 8 bytes on 32-bit, 16 bytes +// on 64-bit. On Linux and Mac, even malloc(1) must reserve a word's worth of +// memory (see Mozilla bug 691003). Mozjemalloc's minimum allocation size is +// 16 bytes, regardless of architecture/OS, which limits the number of +// allocations per page to 256 to support free lists (Bug 1980047). It turns +// out that this has no impact on memory footprint since the size lost due to +// internal fragmentation is offset by better external fragmentation. // Smallest quantum-spaced size classes. It could actually also be labelled a // tiny allocation, and is spaced as such from the largest tiny size class. // Tiny classes being powers of 2, this is twice as large as the largest of // them. -static constexpr size_t kMinQuantumClass = kMaxTinyClass * 2; +static constexpr size_t kMinQuantumClass = 16; static constexpr size_t kMinQuantumWideClass = 512; static constexpr size_t kMinSubPageClass = 4_KiB; @@ -101,10 +97,6 @@ static_assert(kQuantum < kQuantumWide, static_assert(mozilla::IsPowerOfTwo(kMinSubPageClass), "kMinSubPageClass is not a power of two"); -// Number of (2^n)-spaced tiny classes. -static constexpr size_t kNumTinyClasses = - LOG2(kMaxTinyClass) - LOG2(kMinTinyClass) + 1; - // Number of quantum-spaced classes. We add kQuantum(Max) before subtracting to // avoid underflow when a class is empty (Max<Min). static constexpr size_t kNumQuantumClasses = diff --git a/memory/build/Globals.h b/memory/build/Globals.h @@ -124,9 +124,8 @@ void DefineGlobals(); #define PAGE_CEILING(s) (((s) + gPageSizeMask) & ~gPageSizeMask) // Number of all the small-allocated classes -#define NUM_SMALL_CLASSES \ - (kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses + \ - gNumSubPageClasses) +#define NUM_SMALL_CLASSES \ + (kNumQuantumClasses + kNumQuantumWideClasses + gNumSubPageClasses) // Return the chunk address for allocation address a. static inline arena_chunk_t* GetChunkForPtr(const void* aPtr) { diff --git a/memory/build/mozjemalloc.cpp b/memory/build/mozjemalloc.cpp @@ -61,10 +61,7 @@ // | Word size | 32 bit | 64 bit | 64 bit | // | Page size | 4 Kb | 4 Kb | 16 Kb | // |=========================================================| -// | Small | Tiny | 4/-w | -w | - | -// | | | 8 | 8/-w | 8 | -// | |----------------+---------|---------|---------| -// | | Quantum-spaced | 16 | 16 | 16 | +// | Small | Quantum-spaced | 16 | 16 | 16 | // | | | 32 | 32 | 32 | // | | | 48 | 48 | 48 | // | | | ... | ... | ... | @@ -100,14 +97,9 @@ // // Legend: // n: Size class exists for this platform. -// n/-w: This size class doesn't exist on Windows (see kMinTinyClass). // -: This size class doesn't exist for this platform. // ...: Size classes follow a pattern here. // -// NOTE: Due to Mozilla bug 691003, we cannot reserve less than one word for an -// allocation on Linux or Mac. So on 32-bit *nix, the smallest bucket size is -// 4 bytes, and on 64-bit, the smallest bucket size is 8 bytes. -// // A different mechanism is used for each category: // // Small : Each size class is segregated into its own set of runs. Each run @@ -243,7 +235,6 @@ struct arena_stats_t { class SizeClass { public: enum ClassType { - Tiny, Quantum, QuantumWide, SubPage, @@ -251,10 +242,12 @@ class SizeClass { }; explicit inline SizeClass(size_t aSize) { - if (aSize <= kMaxTinyClass) { - mType = Tiny; - mSize = std::max(RoundUpPow2(aSize), kMinTinyClass); - } else if (aSize <= kMaxQuantumClass) { + // We can skip an extra condition here if aSize > 0 and kQuantum >= + // kMinQuantumClass. + MOZ_ASSERT(aSize > 0); + static_assert(kQuantum >= kMinQuantumClass); + + if (aSize <= kMaxQuantumClass) { mType = Quantum; mSize = QUANTUM_CEILING(aSize); } else if (aSize <= kMaxQuantumWideClass) { @@ -2692,25 +2685,18 @@ void* arena_t::MallocSmall(size_t aSize, bool aZero) { aSize = sizeClass.Size(); switch (sizeClass.Type()) { - case SizeClass::Tiny: - bin = &mBins[FloorLog2(aSize / kMinTinyClass)]; - break; case SizeClass::Quantum: // Although we divide 2 things by kQuantum, the compiler will - // reduce `kMinQuantumClass / kQuantum` and `kNumTinyClasses` to a - // single constant. - bin = &mBins[kNumTinyClasses + (aSize / kQuantum) - - (kMinQuantumClass / kQuantum)]; + // reduce `kMinQuantumClass / kQuantum` to a single constant. + bin = &mBins[(aSize / kQuantum) - (kMinQuantumClass / kQuantum)]; break; case SizeClass::QuantumWide: - bin = - &mBins[kNumTinyClasses + kNumQuantumClasses + (aSize / kQuantumWide) - - (kMinQuantumWideClass / kQuantumWide)]; + bin = &mBins[kNumQuantumClasses + (aSize / kQuantumWide) - + (kMinQuantumWideClass / kQuantumWide)]; break; case SizeClass::SubPage: - bin = - &mBins[kNumTinyClasses + kNumQuantumClasses + kNumQuantumWideClasses + - (FloorLog2(aSize) - LOG2(kMinSubPageClass))]; + bin = &mBins[kNumQuantumClasses + kNumQuantumWideClasses + + (FloorLog2(aSize) - LOG2(kMinSubPageClass))]; break; default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected size class type"); @@ -4210,7 +4196,9 @@ inline void* MozJemalloc::valloc(size_t aSize) { // This was added by Mozilla for use by SQLite. inline size_t MozJemalloc::malloc_good_size(size_t aSize) { - if (aSize <= gMaxLargeClass) { + if (aSize == 0) { + aSize = SizeClass(1).Size(); + } else if (aSize <= gMaxLargeClass) { // Small or large aSize = SizeClass(aSize).Size(); } else { diff --git a/memory/build/test/gtest/TestPHC.cpp b/memory/build/test/gtest/TestPHC.cpp @@ -69,13 +69,8 @@ TEST(PHC, TestPHCAllocations) MOZ_CRASH("failed to get a PHC allocation"); } - // On Win64 the smallest possible allocation is 16 bytes. On other platforms - // it is 8 bytes. -#if defined(XP_WIN) && defined(HAVE_64BIT_BUILD) + // The smallest possible allocation is 16 bytes. ASSERT_POS(8U, 16U); -#else - ASSERT_POS(8U, 8U); -#endif for (unsigned i = 16; i <= kPageSize; i *= 2) { ASSERT_POS(i, i); } @@ -98,13 +93,8 @@ TEST(PHC, TestPHCAllocations) ASSERT_EQ(moz_malloc_usable_size(p), (a2)); \ free(p); - // On Win64 the smallest possible allocation is 16 bytes. On other platforms - // it is 8 bytes. -#if defined(XP_WIN) && defined(HAVE_64BIT_BUILD) + // The smallest possible allocation is 16 bytes. ASSERT_ALIGN(8U, 16U); -#else - ASSERT_ALIGN(8U, 8U); -#endif for (unsigned i = 16; i <= kPageSize; i *= 2) { ASSERT_ALIGN(i, i); }