From e18df46092624980a0218982af757a0743cf1d98 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sat, 10 Sep 2016 18:21:20 +0200 Subject: [PATCH] src: notify V8 for low memory when alloc fails Call `v8::Isolate::GetCurrent()->LowMemoryNotification()` when an allocation fails to give V8 a chance to clean up and return memory before retrying (and possibly giving up). Backport-PR-URL: https://github.com/nodejs/node/pull/16587 PR-URL: https://github.com/nodejs/node/pull/8482 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Michael Dawson Reviewed-By: Ilkka Myller --- src/node.cc | 3 +++ src/node_internals.h | 3 +++ src/util-inl.h | 10 +++++++++- src/util.cc | 9 +++++++++ src/util.h | 5 +++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/node.cc b/src/node.cc index 6345abdb312dda..98fde0dbbe3f65 100644 --- a/src/node.cc +++ b/src/node.cc @@ -198,6 +198,7 @@ bool trace_warnings = false; // that is used by lib/module.js bool config_preserve_symlinks = false; +bool v8_initialized = false; // Set in node.cc by ParseArgs when --expose-internals or --expose_internals is // used. @@ -4895,6 +4896,7 @@ int Start(int argc, char** argv) { v8_platform.Initialize(v8_thread_pool_size); V8::Initialize(); + v8_initialized = true; int exit_code = 1; { @@ -4908,6 +4910,7 @@ int Start(int argc, char** argv) { StartNodeInstance(&instance_data); exit_code = instance_data.exit_code(); } + v8_initialized = false; V8::Dispose(); v8_platform.Dispose(); diff --git a/src/node_internals.h b/src/node_internals.h index 130af5d1c0f97f..9ead8b10377075 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -43,6 +43,9 @@ extern std::string openssl_config; // that is used by lib/module.js extern bool config_preserve_symlinks; +// Tells whether it is safe to call v8::Isolate::GetCurrent(). +extern bool v8_initialized; + // Set in node.cc by ParseArgs when --expose-internals or --expose_internals is // used. // Used in node_config.cc to set a constant on process.binding('config') diff --git a/src/util-inl.h b/src/util-inl.h index 886b8569d63d2b..5ffe5b857f5381 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -344,7 +344,15 @@ T* UncheckedRealloc(T* pointer, size_t n) { return nullptr; } - return static_cast(realloc(pointer, full_size)); + void* allocated = realloc(pointer, full_size); + + if (UNLIKELY(allocated == nullptr)) { + // Tell V8 that memory is low and retry. + LowMemoryNotification(); + allocated = realloc(pointer, full_size); + } + + return static_cast(allocated); } // As per spec realloc behaves like malloc if passed nullptr. diff --git a/src/util.cc b/src/util.cc index 14aa68996f56cc..9fb5c3fd2855d3 100644 --- a/src/util.cc +++ b/src/util.cc @@ -77,4 +77,13 @@ BufferValue::BufferValue(Isolate* isolate, Local value) { } } +void LowMemoryNotification() { + if (v8_initialized) { + auto isolate = v8::Isolate::GetCurrent(); + if (isolate != nullptr) { + isolate->LowMemoryNotification(); + } + } +} + } // namespace node diff --git a/src/util.h b/src/util.h index 8b2db6f5c321e8..4ce25e4622f4b2 100644 --- a/src/util.h +++ b/src/util.h @@ -53,6 +53,11 @@ inline char* Calloc(size_t n) { return Calloc(n); } inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc(n); } inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc(n); } +// Used by the allocation functions when allocation fails. +// Thin wrapper around v8::Isolate::LowMemoryNotification() that checks +// whether V8 is initialized. +void LowMemoryNotification(); + #ifdef __GNUC__ #define NO_RETURN __attribute__((noreturn)) #else