Skip to content

Commit

Permalink
🐞 fix: #387 Rewrite getV8HeapStatistics(), getV8HeapSpaceStatistics()…
Browse files Browse the repository at this point in the history
… for V8Runtime again to fix possible JVM crash
  • Loading branch information
caoccao committed Aug 30, 2024
1 parent 5a4f695 commit f77fab5
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 74 deletions.
4 changes: 2 additions & 2 deletions cpp/jni/javet_enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ namespace Javet {

namespace RawPointerType {
enum RawPointerType {
HeapStatisticsContainer = 1,
HeapSpaceStatisticsContainer = 2,
HeapStatisticsContext = 1,
HeapSpaceStatisticsContext = 2,
Invalid = 0,
};
}
Expand Down
8 changes: 4 additions & 4 deletions cpp/jni/javet_jni_core_v8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_removeRawPointer
(JNIEnv* jniEnv, jobject caller, jlong handle, jint rawPointerTypeId) {
using namespace Javet::Enums::RawPointerType;
switch (rawPointerTypeId) {
case HeapStatisticsContainer:
Javet::Monitor::RemoveHeapStatisticsContainer(handle);
case HeapStatisticsContext:
Javet::Monitor::RemoveHeapStatisticsContext(handle);
break;
case HeapSpaceStatisticsContainer:
Javet::Monitor::RemoveHeapSpaceStatisticsContainer(handle);
case HeapSpaceStatisticsContext:
Javet::Monitor::RemoveHeapSpaceStatisticsContext(handle);
break;
default:
break;
Expand Down
139 changes: 102 additions & 37 deletions cpp/jni/javet_monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,40 @@

namespace Javet {
namespace Monitor {
struct HeapSpaceStatisticsContainer {
static jclass jclassV8AllocationSpace;
static jmethodID jmethodIDV8AllocationSpaceGetIndex;

static jclass jclassV8HeapSpaceStatistics;
static jmethodID jmethodIDV8HeapSpaceStatisticsConstructor;
static jmethodID jmethodIDV8HeapSpaceStatisticsSetAllocationSpace;

static jclass jclassV8HeapStatistics;
static jmethodID jmethodIDV8HeapStatisticsConstructor;

static jclass jclassV8Host;
static jmethodID jmethodIDV8HostRegisterV8StatisticsFuture;
static jmethodID jmethodIDV8HostRequestV8StatisticsFuture;

static jclass jclassV8SharedMemoryStatistics;
static jmethodID jmethodIDV8SharedMemoryStatisticsConstructor;

static jclass jclassV8StatisticsFuture;
static jmethodID jmethodIDV8StatisticsFutureConstructor;
static jmethodID jmethodIDV8StatisticsFutureComplete;
static jmethodID jmethodIDV8StatisticsFutureSetHandle;

struct HeapSpaceStatisticsContext {
jobject allocationSpace;
jobject completableFuture;

HeapSpaceStatisticsContainer(JNIEnv* jniEnv, jobject completableFuture, jobject allocationSpace) noexcept {
HeapSpaceStatisticsContext(JNIEnv* jniEnv, jobject completableFuture, jobject allocationSpace) noexcept {
this->allocationSpace = jniEnv->NewGlobalRef(allocationSpace);
INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef);
this->completableFuture = jniEnv->NewGlobalRef(completableFuture);
INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef);
}

~HeapSpaceStatisticsContainer() {
~HeapSpaceStatisticsContext() {
FETCH_JNI_ENV(GlobalJavaVM);
jniEnv->CallVoidMethod(completableFuture, jmethodIDV8StatisticsFutureSetHandle, 0);
jniEnv->DeleteGlobalRef(allocationSpace);
Expand All @@ -42,15 +64,15 @@ namespace Javet {
}
};

struct HeapStatisticsContainer {
struct HeapStatisticsContext {
jobject completableFuture;

HeapStatisticsContainer(JNIEnv* jniEnv, jobject completableFuture) noexcept {
HeapStatisticsContext(JNIEnv* jniEnv, jobject completableFuture) noexcept {
this->completableFuture = jniEnv->NewGlobalRef(completableFuture);
INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef);
}

~HeapStatisticsContainer() {
~HeapStatisticsContext() {
FETCH_JNI_ENV(GlobalJavaVM);
jniEnv->CallVoidMethod(completableFuture, jmethodIDV8StatisticsFutureSetHandle, 0);
jniEnv->DeleteGlobalRef(completableFuture);
Expand All @@ -62,11 +84,6 @@ namespace Javet {
jclassV8AllocationSpace = FIND_CLASS(jniEnv, "com/caoccao/javet/enums/V8AllocationSpace");
jmethodIDV8AllocationSpaceGetIndex = jniEnv->GetMethodID(jclassV8AllocationSpace, "getIndex", "()I");

jclassV8StatisticsFuture = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/monitoring/V8StatisticsFuture");
jmethodIDV8StatisticsFutureConstructor = jniEnv->GetMethodID(jclassV8StatisticsFuture, "<init>", "(I)V");
jmethodIDV8StatisticsFutureComplete = jniEnv->GetMethodID(jclassV8StatisticsFuture, "complete", "(Ljava/lang/Object;)Z");
jmethodIDV8StatisticsFutureSetHandle = jniEnv->GetMethodID(jclassV8StatisticsFuture, "setHandle", "(J)V");

jclassV8HeapSpaceStatistics = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/monitoring/V8HeapSpaceStatistics");
jmethodIDV8HeapSpaceStatisticsConstructor = jniEnv->GetMethodID(jclassV8HeapSpaceStatistics, "<init>", "(Ljava/lang/String;JJJJ)V");
jmethodIDV8HeapSpaceStatisticsSetAllocationSpace = jniEnv->GetMethodID(
Expand All @@ -77,8 +94,17 @@ namespace Javet {
jclassV8HeapStatistics = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/monitoring/V8HeapStatistics");
jmethodIDV8HeapStatisticsConstructor = jniEnv->GetMethodID(jclassV8HeapStatistics, "<init>", "(JJJJJJJJJJJJJJ)V");

jclassV8Host = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/V8Host");
jmethodIDV8HostRegisterV8StatisticsFuture = jniEnv->GetStaticMethodID(jclassV8Host, "registerV8StatisticsFuture", "(Lcom/caoccao/javet/interop/monitoring/V8StatisticsFuture;)V");
jmethodIDV8HostRequestV8StatisticsFuture = jniEnv->GetStaticMethodID(jclassV8Host, "requestV8StatisticsFuture", "(J)Z");

jclassV8SharedMemoryStatistics = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/monitoring/V8SharedMemoryStatistics");
jmethodIDV8SharedMemoryStatisticsConstructor = jniEnv->GetMethodID(jclassV8SharedMemoryStatistics, "<init>", "(JJJ)V");

jclassV8StatisticsFuture = FIND_CLASS(jniEnv, "com/caoccao/javet/interop/monitoring/V8StatisticsFuture");
jmethodIDV8StatisticsFutureConstructor = jniEnv->GetMethodID(jclassV8StatisticsFuture, "<init>", "(I)V");
jmethodIDV8StatisticsFutureComplete = jniEnv->GetMethodID(jclassV8StatisticsFuture, "complete", "(Ljava/lang/Object;)Z");
jmethodIDV8StatisticsFutureSetHandle = jniEnv->GetMethodID(jclassV8StatisticsFuture, "setHandle", "(J)V");
}

jobject GetHeapSpaceStatistics(
Expand All @@ -88,36 +114,61 @@ namespace Javet {
jobject jFuture = jniEnv->NewObject(
jclassV8StatisticsFuture,
jmethodIDV8StatisticsFutureConstructor,
(jint)Javet::Enums::RawPointerType::HeapSpaceStatisticsContainer);
auto containerPointer = new HeapSpaceStatisticsContainer(jniEnv, jFuture, jAllocationSpace);
(jint)Javet::Enums::RawPointerType::HeapSpaceStatisticsContext);
auto contextPointer = new HeapSpaceStatisticsContext(jniEnv, jFuture, jAllocationSpace);
INCREASE_COUNTER(Javet::Monitor::CounterType::New);
jniEnv->CallVoidMethod(jFuture, jmethodIDV8StatisticsFutureSetHandle, TO_JAVA_LONG(containerPointer));
jniEnv->CallVoidMethod(jFuture, jmethodIDV8StatisticsFutureSetHandle, TO_JAVA_LONG(contextPointer));
if (v8Isolate->IsInUse()) {
v8Isolate->RequestInterrupt(GetHeapSpaceStatisticsCallback, containerPointer);
jniEnv->CallStaticVoidMethod(jclassV8Host, jmethodIDV8HostRegisterV8StatisticsFuture, jFuture);
v8Isolate->RequestInterrupt(GetHeapSpaceStatisticsAsync, contextPointer);
}
else {
auto v8Locker = v8::Locker(v8Isolate);
GetHeapSpaceStatisticsCallback(v8Isolate, containerPointer);
GetHeapSpaceStatisticsSync(jniEnv, v8Isolate, contextPointer);
}
return jFuture;
}

void GetHeapSpaceStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept {
void GetHeapSpaceStatisticsAsync(v8::Isolate* v8Isolate, void* data) noexcept {
FETCH_JNI_ENV(GlobalJavaVM);
if (jniEnv->CallStaticBooleanMethod(jclassV8Host, jmethodIDV8HostRequestV8StatisticsFuture, TO_JAVA_LONG(data))) {
GetHeapSpaceStatisticsSync(jniEnv, v8Isolate, data);
}
else {
LOG_DEBUG("Ignore GetHeapSpaceStatisticsAsync().");
}
}

void GetHeapSpaceStatisticsInternal(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
const jobject& completableFuture,
const jobject& allocationSpace) noexcept {
v8::HeapSpaceStatistics heapSpaceStatistics;
auto containerPointer = static_cast<HeapSpaceStatisticsContainer*>(data);
auto index = jniEnv->CallIntMethod(containerPointer->allocationSpace, jmethodIDV8AllocationSpaceGetIndex);
auto index = jniEnv->CallIntMethod(allocationSpace, jmethodIDV8AllocationSpaceGetIndex);
v8Isolate->GetHeapSpaceStatistics(&heapSpaceStatistics, static_cast<size_t>(index));
auto jHeapSpaceStatistics = jniEnv->NewObject(jclassV8HeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsConstructor,
Javet::Converter::ToJavaString(jniEnv, heapSpaceStatistics.space_name()),
static_cast<jlong>(heapSpaceStatistics.physical_space_size()),
static_cast<jlong>(heapSpaceStatistics.space_available_size()),
static_cast<jlong>(heapSpaceStatistics.space_size()),
static_cast<jlong>(heapSpaceStatistics.space_used_size()));
jniEnv->CallObjectMethod(jHeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsSetAllocationSpace, containerPointer->allocationSpace);
jniEnv->CallBooleanMethod(containerPointer->completableFuture, jmethodIDV8StatisticsFutureComplete, jHeapSpaceStatistics);
jniEnv->CallObjectMethod(jHeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsSetAllocationSpace, allocationSpace);
jniEnv->CallBooleanMethod(completableFuture, jmethodIDV8StatisticsFutureComplete, jHeapSpaceStatistics);
jniEnv->DeleteLocalRef(jHeapSpaceStatistics);
delete containerPointer;
}

void GetHeapSpaceStatisticsSync(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
void* data) noexcept {
auto contextPointer = static_cast<HeapSpaceStatisticsContext*>(data);
GetHeapSpaceStatisticsInternal(
jniEnv,
v8Isolate,
contextPointer->completableFuture,
contextPointer->allocationSpace);
delete contextPointer;
INCREASE_COUNTER(Javet::Monitor::CounterType::Delete);
}

Expand All @@ -127,24 +178,33 @@ namespace Javet {
jobject jFuture = jniEnv->NewObject(
jclassV8StatisticsFuture,
jmethodIDV8StatisticsFutureConstructor,
(jint)Javet::Enums::RawPointerType::HeapStatisticsContainer);
auto containerPointer = new HeapStatisticsContainer(jniEnv, jFuture);
(jint)Javet::Enums::RawPointerType::HeapStatisticsContext);
auto contextPointer = new HeapStatisticsContext(jniEnv, jFuture);
INCREASE_COUNTER(Javet::Monitor::CounterType::New);
jniEnv->CallVoidMethod(jFuture, jmethodIDV8StatisticsFutureSetHandle, TO_JAVA_LONG(containerPointer));
jniEnv->CallVoidMethod(jFuture, jmethodIDV8StatisticsFutureSetHandle, TO_JAVA_LONG(contextPointer));
if (v8Isolate->IsInUse()) {
v8Isolate->RequestInterrupt(GetHeapStatisticsCallback, containerPointer);
jniEnv->CallStaticVoidMethod(jclassV8Host, jmethodIDV8HostRegisterV8StatisticsFuture, jFuture);
v8Isolate->RequestInterrupt(GetHeapStatisticsAsync, contextPointer);
}
else {
auto v8Locker = v8::Locker(v8Isolate);
GetHeapStatisticsCallback(v8Isolate, containerPointer);
GetHeapStatisticsSync(jniEnv, v8Isolate, contextPointer);
}
return jFuture;
}

void GetHeapStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept {
void GetHeapStatisticsAsync(v8::Isolate* v8Isolate, void* data) noexcept {
FETCH_JNI_ENV(GlobalJavaVM);
if (jniEnv->CallStaticBooleanMethod(jclassV8Host, jmethodIDV8HostRequestV8StatisticsFuture, TO_JAVA_LONG(data))) {
GetHeapStatisticsSync(jniEnv, v8Isolate, data);
}
else {
LOG_DEBUG("Ignore GetHeapStatisticsAsync().");
}
}

void GetHeapStatisticsInternal(JNIEnv* jniEnv, v8::Isolate* v8Isolate, const jobject& completableFuture) noexcept {
v8::HeapStatistics heapStatistics;
auto containerPointer = static_cast<HeapStatisticsContainer*>(data);
v8Isolate->GetHeapStatistics(&heapStatistics);
auto jHeapStatistics = jniEnv->NewObject(jclassV8HeapStatistics, jmethodIDV8HeapStatisticsConstructor,
static_cast<jlong>(heapStatistics.does_zap_garbage()),
Expand All @@ -161,9 +221,14 @@ namespace Javet {
static_cast<jlong>(heapStatistics.total_physical_size()),
static_cast<jlong>(heapStatistics.used_global_handles_size()),
static_cast<jlong>(heapStatistics.used_heap_size()));
jniEnv->CallBooleanMethod(containerPointer->completableFuture, jmethodIDV8StatisticsFutureComplete, jHeapStatistics);
jniEnv->CallBooleanMethod(completableFuture, jmethodIDV8StatisticsFutureComplete, jHeapStatistics);
jniEnv->DeleteLocalRef(jHeapStatistics);
delete containerPointer;
}

void GetHeapStatisticsSync(JNIEnv* jniEnv, v8::Isolate* v8Isolate, void* data) noexcept {
auto contextPointer = static_cast<HeapStatisticsContext*>(data);
GetHeapStatisticsInternal(jniEnv, v8Isolate, contextPointer->completableFuture);
delete contextPointer;
INCREASE_COUNTER(Javet::Monitor::CounterType::Delete);
}

Expand All @@ -176,15 +241,15 @@ namespace Javet {
static_cast<jlong>(sharedMemoryStatistics.read_only_space_used_size()));
}

void RemoveHeapSpaceStatisticsContainer(jlong handle) noexcept {
auto containerPointer = reinterpret_cast<HeapSpaceStatisticsContainer*>(handle);
delete containerPointer;
void RemoveHeapSpaceStatisticsContext(jlong handle) noexcept {
auto contextPointer = reinterpret_cast<HeapSpaceStatisticsContext*>(handle);
delete contextPointer;
INCREASE_COUNTER(Javet::Monitor::CounterType::Delete);
}

void RemoveHeapStatisticsContainer(jlong handle) noexcept {
auto containerPointer = reinterpret_cast<HeapStatisticsContainer*>(handle);
delete containerPointer;
void RemoveHeapStatisticsContext(jlong handle) noexcept {
auto contextPointer = reinterpret_cast<HeapStatisticsContext*>(handle);
delete contextPointer;
INCREASE_COUNTER(Javet::Monitor::CounterType::Delete);
}

Expand Down
45 changes: 21 additions & 24 deletions cpp/jni/javet_monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,38 @@

namespace Javet {
namespace Monitor {
static jclass jclassV8AllocationSpace;
static jmethodID jmethodIDV8AllocationSpaceGetIndex;

static jclass jclassV8StatisticsFuture;
static jmethodID jmethodIDV8StatisticsFutureConstructor;
static jmethodID jmethodIDV8StatisticsFutureComplete;
static jmethodID jmethodIDV8StatisticsFutureSetHandle;

static jclass jclassV8HeapSpaceStatistics;
static jmethodID jmethodIDV8HeapSpaceStatisticsConstructor;
static jmethodID jmethodIDV8HeapSpaceStatisticsSetAllocationSpace;

static jclass jclassV8HeapStatistics;
static jmethodID jmethodIDV8HeapStatisticsConstructor;

static jclass jclassV8SharedMemoryStatistics;
static jmethodID jmethodIDV8SharedMemoryStatisticsConstructor;

void Initialize(JNIEnv* jniEnv) noexcept;

jobject GetHeapSpaceStatistics(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
const jobject allocationSpaceIndex) noexcept;
void GetHeapSpaceStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept;
void GetHeapSpaceStatisticsAsync(v8::Isolate* v8Isolate, void* data) noexcept;
void GetHeapSpaceStatisticsInternal(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
const jobject& completableFuture,
const jobject& allocationSpace) noexcept;
void GetHeapSpaceStatisticsSync(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
void* data) noexcept;

jobject GetHeapStatistics(
jobject GetHeapStatistics(JNIEnv* jniEnv, v8::Isolate* v8Isolate) noexcept;
void GetHeapStatisticsAsync(v8::Isolate* v8Isolate, void* data) noexcept;
void GetHeapStatisticsInternal(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate) noexcept;
void GetHeapStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept;
v8::Isolate* v8Isolate,
const jobject& completableFuture) noexcept;
void GetHeapStatisticsSync(
JNIEnv* jniEnv,
v8::Isolate* v8Isolate,
void* data) noexcept;

jobject GetV8SharedMemoryStatistics(JNIEnv* jniEnv) noexcept;

void RemoveHeapSpaceStatisticsContainer(jlong handle) noexcept;
void RemoveHeapStatisticsContainer(jlong handle) noexcept;
void RemoveHeapSpaceStatisticsContext(jlong handle) noexcept;
void RemoveHeapStatisticsContext(jlong handle) noexcept;

#ifdef ENABLE_MONITOR
namespace CounterType {
Expand Down
7 changes: 6 additions & 1 deletion docs/release_notes/release_notes_3_1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
Release Notes 3.1.x
===================

3.1.6 V8 v12.9
--------------

* Rewrote ``getV8HeapStatistics()``, ``getV8HeapSpaceStatistics()`` for ``V8Runtime`` again to fix possible JVM crash

3.1.5 V8 v12.8
--------------

* Upgraded Node.js to ``v20.16.0`` `(2024-07-24) <https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V20.md#20.16.0>`_
* Upgraded V8 to ``v12.8.374.17`` (2024-08-19)
* Upgraded Android NDK to r27
* Fixed a bug of the default export in ``JavetBuiltInModuleResolver``
* Adjust JS type conversion priority for better performance
* Adjusted JS type conversion priority for better performance

3.1.4 V8 v12.7
--------------
Expand Down
Loading

0 comments on commit f77fab5

Please sign in to comment.