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:
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);
}