Bug 999158 - Keep a spare chunk around to mitigate GGC OOM crashes on tenuring r=terrence

This commit is contained in:
Jon Coppeard 2014-07-14 10:05:17 +01:00
Родитель b59461402a
Коммит 81b32d0f45
2 изменённых файлов: 20 добавлений и 15 удалений

Просмотреть файл

@ -55,9 +55,6 @@ class ChunkPool
/* Must be called either during the GC or with the GC lock taken. */ /* Must be called either during the GC or with the GC lock taken. */
inline void put(Chunk *chunk); inline void put(Chunk *chunk);
/* Must be called with the GC lock taken. */
void expireAndFree(JSRuntime *rt, bool releaseAll);
class Enum { class Enum {
public: public:
Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {} Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {}
@ -358,7 +355,7 @@ class GCRuntime
* Return the list of chunks that can be released outside the GC lock. * Return the list of chunks that can be released outside the GC lock.
* Must be called either during the GC or with the GC lock taken. * Must be called either during the GC or with the GC lock taken.
*/ */
Chunk *expireChunkPool(bool releaseAll); Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
void expireAndFreeChunkPool(bool releaseAll); void expireAndFreeChunkPool(bool releaseAll);
void freeChunkList(Chunk *chunkListHead); void freeChunkList(Chunk *chunkListHead);
void prepareToFreeChunk(ChunkInfo &info); void prepareToFreeChunk(ChunkInfo &info);

Просмотреть файл

@ -239,10 +239,16 @@ static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
/* Increase the IGC marking slice time if we are in highFrequencyGC mode. */ /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
static const int IGC_MARK_SLICE_MULTIPLIER = 2; static const int IGC_MARK_SLICE_MULTIPLIER = 2;
#if defined(ANDROID) || defined(MOZ_B2G) #ifdef JSGC_GENERATIONAL
static const int MAX_EMPTY_CHUNK_COUNT = 2; static const unsigned MIN_EMPTY_CHUNK_COUNT = 1;
#else #else
static const int MAX_EMPTY_CHUNK_COUNT = 30; static const unsigned MIN_EMPTY_CHUNK_COUNT = 0;
#endif
#if defined(ANDROID) || defined(MOZ_B2G)
static const unsigned MAX_EMPTY_CHUNK_COUNT = 2;
#else
static const unsigned MAX_EMPTY_CHUNK_COUNT = 30;
#endif #endif
const AllocKind gc::slotsToThingKind[] = { const AllocKind gc::slotsToThingKind[] = {
@ -712,7 +718,7 @@ ChunkPool::Enum::removeAndPopFront()
/* Must be called either during the GC or with the GC lock taken. */ /* Must be called either during the GC or with the GC lock taken. */
Chunk * Chunk *
GCRuntime::expireChunkPool(bool releaseAll) GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
{ {
/* /*
* Return old empty chunks to the system while preserving the order of * Return old empty chunks to the system while preserving the order of
@ -721,14 +727,14 @@ GCRuntime::expireChunkPool(bool releaseAll)
* and are more likely to reach the max age. * and are more likely to reach the max age.
*/ */
Chunk *freeList = nullptr; Chunk *freeList = nullptr;
int freeChunkCount = 0; unsigned freeChunkCount = 0;
for (ChunkPool::Enum e(chunkPool); !e.empty(); ) { for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
Chunk *chunk = e.front(); Chunk *chunk = e.front();
JS_ASSERT(chunk->unused()); JS_ASSERT(chunk->unused());
JS_ASSERT(!chunkSet.has(chunk)); JS_ASSERT(!chunkSet.has(chunk));
JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); if (releaseAll || freeChunkCount >= MAX_EMPTY_CHUNK_COUNT ||
if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE || (freeChunkCount >= MIN_EMPTY_CHUNK_COUNT &&
freeChunkCount++ > MAX_EMPTY_CHUNK_COUNT) (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
{ {
e.removeAndPopFront(); e.removeAndPopFront();
prepareToFreeChunk(chunk->info); prepareToFreeChunk(chunk->info);
@ -736,10 +742,12 @@ GCRuntime::expireChunkPool(bool releaseAll)
freeList = chunk; freeList = chunk;
} else { } else {
/* Keep the chunk but increase its age. */ /* Keep the chunk but increase its age. */
++freeChunkCount;
++chunk->info.age; ++chunk->info.age;
e.popFront(); e.popFront();
} }
} }
JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= MIN_EMPTY_CHUNK_COUNT);
JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0); JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
return freeList; return freeList;
} }
@ -757,7 +765,7 @@ GCRuntime::freeChunkList(Chunk *chunkListHead)
void void
GCRuntime::expireAndFreeChunkPool(bool releaseAll) GCRuntime::expireAndFreeChunkPool(bool releaseAll)
{ {
freeChunkList(expireChunkPool(releaseAll)); freeChunkList(expireChunkPool(true, releaseAll));
} }
/* static */ Chunk * /* static */ Chunk *
@ -1056,7 +1064,7 @@ GCRuntime::wantBackgroundAllocation() const
* of them. * of them.
*/ */
return helperState.canBackgroundAllocate() && return helperState.canBackgroundAllocate() &&
chunkPool.getEmptyCount() == 0 && chunkPool.getEmptyCount() < MIN_EMPTY_CHUNK_COUNT &&
chunkSet.count() >= 4; chunkSet.count() >= 4;
} }
@ -2609,7 +2617,7 @@ GCRuntime::expireChunksAndArenas(bool shouldShrink)
rt->threadPool.pruneChunkCache(); rt->threadPool.pruneChunkCache();
#endif #endif
if (Chunk *toFree = expireChunkPool(shouldShrink)) { if (Chunk *toFree = expireChunkPool(shouldShrink, false)) {
AutoUnlockGC unlock(rt); AutoUnlockGC unlock(rt);
freeChunkList(toFree); freeChunkList(toFree);
} }