[Memory] Add OnCriticalMemoryPressure overload to v8::Platform.
- Adds overload to v8::Platform that will make it easier for embedders to maintain a reserve of address space for large, contiguous allocations. - Rewrites retry logic using loops. - Moves retry logic from some VirtualMemory allocation functions to AllocPages. Bug: chromium:756050 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I52e66f9f8b15b6ce2a2f36e74783f178b8cd5cf7 Reviewed-on: https://chromium-review.googlesource.com/840724 Commit-Queue: Bill Budge <bbudge@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#50303}
This commit is contained in:
parent
c8736f6899
commit
29bb707e9b
@ -158,7 +158,7 @@ class TracingController {
|
||||
};
|
||||
|
||||
/**
|
||||
* A V8 page memory allocator.
|
||||
* A V8 memory page allocator.
|
||||
*
|
||||
* Can be implemented by an embedder to manage large host OS allocations.
|
||||
*/
|
||||
@ -246,7 +246,7 @@ class Platform {
|
||||
virtual ~Platform() = default;
|
||||
|
||||
/**
|
||||
* Allows the embedder to manage large memory allocations.
|
||||
* Allows the embedder to manage memory page allocations.
|
||||
*/
|
||||
virtual PageAllocator* GetPageAllocator() {
|
||||
// TODO(bbudge) Make this abstract after all embedders implement this.
|
||||
@ -261,12 +261,21 @@ class Platform {
|
||||
* Embedder overrides of this function must NOT call back into V8.
|
||||
*/
|
||||
virtual void OnCriticalMemoryPressure() {
|
||||
// TODO(bbudge) Change this method by adding a failed_allocation_size
|
||||
// parameter, and to return a bool. This will allow embedders to manage a
|
||||
// reserve, rather than simply release it all on a failure.
|
||||
// TODO(bbudge) Remove this when embedders override the following method.
|
||||
// See crbug.com/634547.
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the embedder to respond in cases where V8 can't allocate large
|
||||
* memory regions. The |length| parameter is the amount of memory needed.
|
||||
* Returns true if memory is now available. Returns false if no memory could
|
||||
* be made available. V8 will retry allocations until this method returns
|
||||
* false.
|
||||
*
|
||||
* Embedder overrides of this function must NOT call back into V8.
|
||||
*/
|
||||
virtual bool OnCriticalMemoryPressure(size_t length) { return false; }
|
||||
|
||||
/**
|
||||
* Gets the number of threads that are used to execute background tasks. Is
|
||||
* used to estimate the number of tasks a work package should be split into.
|
||||
|
@ -60,16 +60,21 @@ static base::LazyInstance<v8::PageAllocator*, InitializePageAllocator>::type
|
||||
|
||||
v8::PageAllocator* GetPageAllocator() { return page_allocator.Get(); }
|
||||
|
||||
// We will attempt allocation this many times. After each failure, we call
|
||||
// OnCriticalMemoryPressure to try to free some memory.
|
||||
const int kAllocationTries = 2;
|
||||
|
||||
} // namespace
|
||||
|
||||
void* Malloced::New(size_t size) {
|
||||
void* result = malloc(size);
|
||||
if (result == nullptr) {
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
void* result = nullptr;
|
||||
for (int i = 0; i < kAllocationTries; ++i) {
|
||||
result = malloc(size);
|
||||
if (result == nullptr) {
|
||||
V8::FatalProcessOutOfMemory("Malloced operator new");
|
||||
}
|
||||
if (result != nullptr) break;
|
||||
if (!OnCriticalMemoryPressure(size)) break;
|
||||
}
|
||||
if (result == nullptr) {
|
||||
V8::FatalProcessOutOfMemory("Malloced operator new");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -102,15 +107,16 @@ char* StrNDup(const char* str, int n) {
|
||||
void* AlignedAlloc(size_t size, size_t alignment) {
|
||||
DCHECK_LE(V8_ALIGNOF(void*), alignment);
|
||||
DCHECK(base::bits::IsPowerOfTwo(alignment));
|
||||
void* ptr = AlignedAllocInternal(size, alignment);
|
||||
if (ptr == nullptr) {
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
ptr = AlignedAllocInternal(size, alignment);
|
||||
if (ptr == nullptr) {
|
||||
V8::FatalProcessOutOfMemory("AlignedAlloc");
|
||||
}
|
||||
void* result = nullptr;
|
||||
for (int i = 0; i < kAllocationTries; ++i) {
|
||||
result = AlignedAllocInternal(size, alignment);
|
||||
if (result != nullptr) break;
|
||||
if (!OnCriticalMemoryPressure(size + alignment)) break;
|
||||
}
|
||||
return ptr;
|
||||
if (result == nullptr) {
|
||||
V8::FatalProcessOutOfMemory("AlignedAlloc");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -137,8 +143,14 @@ void* GetRandomMmapAddr() { return GetPageAllocator()->GetRandomMmapAddr(); }
|
||||
|
||||
void* AllocatePages(void* address, size_t size, size_t alignment,
|
||||
PageAllocator::Permission access) {
|
||||
void* result =
|
||||
GetPageAllocator()->AllocatePages(address, size, alignment, access);
|
||||
void* result = nullptr;
|
||||
for (int i = 0; i < kAllocationTries; ++i) {
|
||||
result =
|
||||
GetPageAllocator()->AllocatePages(address, size, alignment, access);
|
||||
if (result != nullptr) break;
|
||||
size_t request_size = size + alignment - AllocatePageSize();
|
||||
if (!OnCriticalMemoryPressure(request_size)) break;
|
||||
}
|
||||
#if defined(LEAK_SANITIZER)
|
||||
if (result != nullptr) {
|
||||
__lsan_register_root_region(result, size);
|
||||
@ -182,6 +194,15 @@ byte* AllocatePage(void* address, size_t* allocated) {
|
||||
return static_cast<byte*>(result);
|
||||
}
|
||||
|
||||
bool OnCriticalMemoryPressure(size_t length) {
|
||||
// TODO(bbudge) Rework retry logic once embedders implement the more
|
||||
// informative overload.
|
||||
if (!V8::GetCurrentPlatform()->OnCriticalMemoryPressure(length)) {
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VirtualMemory::VirtualMemory() : address_(nullptr), size_(0) {}
|
||||
|
||||
VirtualMemory::VirtualMemory(size_t size, void* hint, size_t alignment)
|
||||
@ -250,30 +271,22 @@ void VirtualMemory::TakeControl(VirtualMemory* from) {
|
||||
}
|
||||
|
||||
bool AllocVirtualMemory(size_t size, void* hint, VirtualMemory* result) {
|
||||
VirtualMemory first_try(size, hint);
|
||||
if (first_try.IsReserved()) {
|
||||
result->TakeControl(&first_try);
|
||||
VirtualMemory vm(size, hint);
|
||||
if (vm.IsReserved()) {
|
||||
result->TakeControl(&vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
VirtualMemory second_try(size, hint);
|
||||
result->TakeControl(&second_try);
|
||||
return result->IsReserved();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AlignedAllocVirtualMemory(size_t size, size_t alignment, void* hint,
|
||||
VirtualMemory* result) {
|
||||
VirtualMemory first_try(size, hint, alignment);
|
||||
if (first_try.IsReserved()) {
|
||||
result->TakeControl(&first_try);
|
||||
VirtualMemory vm(size, hint, alignment);
|
||||
if (vm.IsReserved()) {
|
||||
result->TakeControl(&vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
VirtualMemory second_try(size, hint, alignment);
|
||||
result->TakeControl(&second_try);
|
||||
return result->IsReserved();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -127,6 +127,11 @@ V8_WARN_UNUSED_RESULT bool SetPermissions(void* address, size_t size,
|
||||
V8_EXPORT_PRIVATE
|
||||
V8_WARN_UNUSED_RESULT byte* AllocatePage(void* address, size_t* allocated);
|
||||
|
||||
// Function that may release reserved memory regions to allow failed allocations
|
||||
// to succeed. |length| is the amount of memory needed. Returns |true| if memory
|
||||
// could be released, false otherwise.
|
||||
V8_EXPORT_PRIVATE bool OnCriticalMemoryPressure(size_t length);
|
||||
|
||||
// Represents and controls an area of reserved memory.
|
||||
class V8_EXPORT_PRIVATE VirtualMemory {
|
||||
public:
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <malloc.h> // NOLINT
|
||||
#endif
|
||||
|
||||
#include "src/allocation.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@ -82,10 +84,12 @@ Segment* AccountingAllocator::GetSegment(size_t bytes) {
|
||||
}
|
||||
|
||||
Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
|
||||
void* memory = malloc(bytes);
|
||||
if (memory == nullptr) {
|
||||
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
|
||||
const int kAllocationTries = 2;
|
||||
void* memory = nullptr;
|
||||
for (int i = 0; i < kAllocationTries; ++i) {
|
||||
memory = malloc(bytes);
|
||||
if (memory != nullptr) break;
|
||||
if (!OnCriticalMemoryPressure(bytes)) break;
|
||||
}
|
||||
if (memory != nullptr) {
|
||||
base::AtomicWord current =
|
||||
|
Loading…
Reference in New Issue
Block a user