Bug 1388935 - Avoid overflowing the chunk recycling limit with very large chunks. r?njn
When recycling chunks, mozjemalloc tries to avoid keeping around more
than 128MB worth of chunks around, but it doesn't actually look at the
size of the chunks that are recycled, so if chunk larger than 128MB is
recycled, it is kept as a whole, going well over the limit.
The chunks are still properly reused, and further recycling doesn't
occur, but that can limit other mmap users from getting enough address
space.
With this change, mozjemalloc now doesn't keep more than 128MB, by
splitting the chunks it recycles if they are too large.
Note this was not a problem on Windows, where chunks larger than 1MB are
never recycled (per CAN_RECYCLE).
--- a/memory/mozjemalloc/mozjemalloc.cpp
+++ b/memory/mozjemalloc/mozjemalloc.cpp
@@ -135,25 +135,25 @@
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+#include <algorithm>
#ifdef MOZ_MEMORY_WINDOWS
/* Some defines from the CRT internal headers that we need here. */
#define _CRT_SPINCOUNT 5000
#include <io.h>
#include <windows.h>
#include <intrin.h>
-#include <algorithm>
#define SIZE_T_MAX SIZE_MAX
#define STDERR_FILENO 2
#ifndef NO_TLS
static unsigned long tlsIndex = 0xffffffff;
#endif
@@ -2176,19 +2176,32 @@ chunk_dealloc(void *chunk, size_t size,
MOZ_ASSERT(chunk);
MOZ_ASSERT(CHUNK_ADDR2BASE(chunk) == chunk);
MOZ_ASSERT(size != 0);
MOZ_ASSERT((size & chunksize_mask) == 0);
malloc_rtree_set(chunk_rtree, (uintptr_t)chunk, nullptr);
- if (CAN_RECYCLE(size) && load_acquire_z(&recycled_size) < recycle_limit) {
- chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size, type);
- return;
+ if (CAN_RECYCLE(size)) {
+ size_t recycled_so_far = load_acquire_z(&recycled_size);
+ // In case some race condition put us above the limit.
+ if (recycled_so_far < recycle_limit) {
+ size_t recycle_remaining = recycle_limit - recycled_so_far;
+ size_t to_recycle;
+ if (size > recycle_remaining) {
+ to_recycle = recycle_remaining;
+ // Drop pages that would overflow the recycle limit
+ pages_trim(chunk, size, 0, to_recycle);
+ } else {
+ to_recycle = size;
+ }
+ chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, to_recycle, type);
+ return;
+ }
}
pages_unmap(chunk, size);
}
#undef CAN_RECYCLE
/*