diff --git a/cpp/jni/javet_monitor.cpp b/cpp/jni/javet_monitor.cpp index ac95eea1f..f469ccc9b 100644 --- a/cpp/jni/javet_monitor.cpp +++ b/cpp/jni/javet_monitor.cpp @@ -21,6 +21,41 @@ namespace Javet { namespace Monitor { + struct HeapSpaceStatisticsContainer { + jobject allocationSpace; + jobject completableFuture; + + HeapSpaceStatisticsContainer(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() { + FETCH_JNI_ENV(GlobalJavaVM); + jniEnv->DeleteGlobalRef(allocationSpace); + INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); + jniEnv->DeleteGlobalRef(completableFuture); + INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); + } + }; + + struct HeapStatisticsContainer { + jobject completableFuture; + + HeapStatisticsContainer(JNIEnv* jniEnv, jobject completableFuture) noexcept { + this->completableFuture = jniEnv->NewGlobalRef(completableFuture); + INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef); + } + + ~HeapStatisticsContainer() { + FETCH_JNI_ENV(GlobalJavaVM); + jniEnv->DeleteGlobalRef(completableFuture); + INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); + } + }; + void Initialize(JNIEnv* jniEnv) noexcept { jclassV8AllocationSpace = FIND_CLASS(jniEnv, "com/caoccao/javet/enums/V8AllocationSpace"); jmethodIDV8AllocationSpaceGetIndex = jniEnv->GetMethodID(jclassV8AllocationSpace, "getIndex", "()I"); @@ -48,16 +83,14 @@ namespace Javet { v8::Isolate* v8Isolate, const jobject jAllocationSpace) noexcept { jobject jCompletableFuture = jniEnv->NewObject(jclassCompletableFuture, jmethodIDCompletableFutureConstructor); - auto jobjectRefs = new jobject[]{ jniEnv->NewGlobalRef(jCompletableFuture), jniEnv->NewGlobalRef(jAllocationSpace) }; + auto containerPointer = new HeapSpaceStatisticsContainer(jniEnv, jCompletableFuture, jAllocationSpace); INCREASE_COUNTER(Javet::Monitor::CounterType::New); - INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef); - INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef); if (v8Isolate->IsInUse()) { - v8Isolate->RequestInterrupt(GetHeapSpaceStatisticsCallback, &jobjectRefs); + v8Isolate->RequestInterrupt(GetHeapSpaceStatisticsCallback, containerPointer); } else { auto v8Locker = v8::Locker(v8Isolate); - GetHeapSpaceStatisticsCallback(v8Isolate, jobjectRefs); + GetHeapSpaceStatisticsCallback(v8Isolate, containerPointer); } return jCompletableFuture; } @@ -65,10 +98,8 @@ namespace Javet { void GetHeapSpaceStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept { FETCH_JNI_ENV(GlobalJavaVM); v8::HeapSpaceStatistics heapSpaceStatistics; - auto jobjectRefs = static_cast(data); - auto jCompletableFuture = jobjectRefs[0]; - auto jAllocationSpace = jobjectRefs[1]; - auto index = jniEnv->CallIntMethod(jAllocationSpace, jmethodIDV8AllocationSpaceGetIndex); + auto containerPointer = static_cast(data); + auto index = jniEnv->CallIntMethod(containerPointer->allocationSpace, jmethodIDV8AllocationSpaceGetIndex); v8Isolate->GetHeapSpaceStatistics(&heapSpaceStatistics, static_cast(index)); auto jHeapSpaceStatistics = jniEnv->NewObject(jclassV8HeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsConstructor, Javet::Converter::ToJavaString(jniEnv, heapSpaceStatistics.space_name()), @@ -76,14 +107,10 @@ namespace Javet { static_cast(heapSpaceStatistics.space_available_size()), static_cast(heapSpaceStatistics.space_size()), static_cast(heapSpaceStatistics.space_used_size())); - jniEnv->CallObjectMethod(jHeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsSetAllocationSpace, jAllocationSpace); - jniEnv->CallBooleanMethod(jCompletableFuture, jmethodIDCompletableFutureComplete, jHeapSpaceStatistics); + jniEnv->CallObjectMethod(jHeapSpaceStatistics, jmethodIDV8HeapSpaceStatisticsSetAllocationSpace, containerPointer->allocationSpace); + jniEnv->CallBooleanMethod(containerPointer->completableFuture, jmethodIDCompletableFutureComplete, jHeapSpaceStatistics); jniEnv->DeleteLocalRef(jHeapSpaceStatistics); - jniEnv->DeleteGlobalRef(jCompletableFuture); - INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); - jniEnv->DeleteGlobalRef(jAllocationSpace); - INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); - delete jobjectRefs; + delete containerPointer; INCREASE_COUNTER(Javet::Monitor::CounterType::Delete); } @@ -91,15 +118,14 @@ namespace Javet { JNIEnv* jniEnv, v8::Isolate* v8Isolate) noexcept { jobject jCompletableFuture = jniEnv->NewObject(jclassCompletableFuture, jmethodIDCompletableFutureConstructor); - auto jobjectRefs = new jobject[]{ jniEnv->NewGlobalRef(jCompletableFuture) }; + auto containerPointer = new HeapStatisticsContainer(jniEnv, jCompletableFuture); INCREASE_COUNTER(Javet::Monitor::CounterType::New); - INCREASE_COUNTER(Javet::Monitor::CounterType::NewGlobalRef); if (v8Isolate->IsInUse()) { - v8Isolate->RequestInterrupt(GetHeapStatisticsCallback, jobjectRefs); + v8Isolate->RequestInterrupt(GetHeapStatisticsCallback, containerPointer); } else { auto v8Locker = v8::Locker(v8Isolate); - GetHeapStatisticsCallback(v8Isolate, jobjectRefs); + GetHeapStatisticsCallback(v8Isolate, containerPointer); } return jCompletableFuture; } @@ -107,8 +133,7 @@ namespace Javet { void GetHeapStatisticsCallback(v8::Isolate* v8Isolate, void* data) noexcept { FETCH_JNI_ENV(GlobalJavaVM); v8::HeapStatistics heapStatistics; - auto jobjectRefs = static_cast(data); - auto jCompletableFuture = jobjectRefs[0]; + auto containerPointer = static_cast(data); v8Isolate->GetHeapStatistics(&heapStatistics); auto jHeapStatistics = jniEnv->NewObject(jclassV8HeapStatistics, jmethodIDV8HeapStatisticsConstructor, static_cast(heapStatistics.does_zap_garbage()), @@ -125,11 +150,9 @@ namespace Javet { static_cast(heapStatistics.total_physical_size()), static_cast(heapStatistics.used_global_handles_size()), static_cast(heapStatistics.used_heap_size())); - jniEnv->CallBooleanMethod(jCompletableFuture, jmethodIDCompletableFutureComplete, jHeapStatistics); + jniEnv->CallBooleanMethod(containerPointer->completableFuture, jmethodIDCompletableFutureComplete, jHeapStatistics); jniEnv->DeleteLocalRef(jHeapStatistics); - jniEnv->DeleteGlobalRef(jCompletableFuture); - INCREASE_COUNTER(Javet::Monitor::CounterType::DeleteGlobalRef); - delete jobjectRefs; + delete containerPointer; INCREASE_COUNTER(Javet::Monitor::CounterType::Delete); } diff --git a/docs/reference/resource_management/memory_management.rst b/docs/reference/resource_management/memory_management.rst index 6673c73bb..360b2dab3 100644 --- a/docs/reference/resource_management/memory_management.rst +++ b/docs/reference/resource_management/memory_management.rst @@ -256,4 +256,5 @@ V8 exposes quite a few statistics for applications to analyze the memory usage, .. note:: - More statistics will be exposed in new releases. Please file issues if you need more of them. + * If the ``V8Runtime`` is in use, calling ``getV8HeapSpaceStatistics()`` and ``getV8HeapStatistics()`` may take a slight chance (a race condition) to have tiny memory leak. Please refer to this `issue `_ for details. It's recommended to call them when the ``V8Runtime`` is idle. + * More statistics will be exposed in new releases. Please file issues if you need more of them. diff --git a/docs/release_notes/release_notes_3_1.rst b/docs/release_notes/release_notes_3_1.rst index 86abe0e34..cfb3242f1 100644 --- a/docs/release_notes/release_notes_3_1.rst +++ b/docs/release_notes/release_notes_3_1.rst @@ -6,6 +6,7 @@ Release Notes 3.1.x -------------- * Rewrote ``getV8HeapStatistics()``, ``getV8HeapSpaceStatistics()`` for ``V8Runtime`` to remediate the racing condition +* Added ``observerTimeoutMillis`` to ``JavetEngineConfig`` 3.1.3 V8 v12.6 -------------- diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java index d50a72e35..8adcb1317 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java @@ -20,7 +20,6 @@ import com.caoccao.javet.enums.V8AllocationSpace; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interfaces.IJavetClosable; -import com.caoccao.javet.interop.V8Host; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.interop.engine.observers.*; import com.caoccao.javet.interop.monitoring.V8HeapSpaceStatistics; @@ -77,7 +76,9 @@ default int getAverageReferenceCount() { */ default V8HeapSpaceStatistics getAverageV8HeapSpaceStatistics(final V8AllocationSpace v8AllocationSpace) { V8RuntimeObserverAverageV8HeapSpaceStatistics observer = new V8RuntimeObserverAverageV8HeapSpaceStatistics( - v8AllocationSpace, getConfig().getPoolMaxSize()); + v8AllocationSpace, + getConfig().getPoolMaxSize(), + getConfig().getObserverTimeoutMillis()); observe(observer); return observer.getResult(); } @@ -90,7 +91,8 @@ default V8HeapSpaceStatistics getAverageV8HeapSpaceStatistics(final V8Allocation */ default V8HeapStatistics getAverageV8HeapStatistics() { V8RuntimeObserverAverageV8HeapStatistics observer = new V8RuntimeObserverAverageV8HeapStatistics( - getConfig().getPoolMaxSize()); + getConfig().getPoolMaxSize(), + getConfig().getObserverTimeoutMillis()); observe(observer); return observer.getResult(); } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java index 37fbc01af..14d449686 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java @@ -37,6 +37,12 @@ public final class JavetEngineConfig { * @since 0.8.0 */ public static final JSRuntimeType DEFAULT_JS_RUNTIME_TYPE = JSRuntimeType.V8; + /** + * The constant DEFAULT_OBSERVER_TIMEOUT_MILLIS. + * + * @since 3.1.4 + */ + public static final int DEFAULT_OBSERVER_TIMEOUT_MILLIS = 5000; /** * The constant DEFAULT_POOL_MIN_SIZE. * @@ -55,12 +61,6 @@ public final class JavetEngineConfig { * @since 0.7.0 */ public static final int DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS = 1000; - /** - * The constant DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS. - * - * @since 0.7.0 - */ - public static final int DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS = 3600; /** * The constant DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS. * @@ -68,23 +68,29 @@ public final class JavetEngineConfig { */ public static final int DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS = 5; /** - * The constant DEFAULT_WAIT_FOR_ENGINE_LOG_INTERVAL_MILLIS. + * The constant DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS. * - * @since 1.0.5 + * @since 0.7.0 */ - public static final int DEFAULT_WAIT_FOR_ENGINE_LOG_INTERVAL_MILLIS = 1000; + public static final int DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS = 3600; /** - * The constant MAX_POOL_SIZE. + * The constant DEFAULT_WAIT_FOR_ENGINE_LOG_INTERVAL_MILLIS. * - * @since 1.0.4 + * @since 1.0.5 */ - public static final int MAX_POOL_SIZE = 4096; + public static final int DEFAULT_WAIT_FOR_ENGINE_LOG_INTERVAL_MILLIS = 1000; /** * The constant DEFAULT_WAIT_FOR_ENGINE_MAX_RETRY_COUNT. * * @since 1.1.6 */ public static final int DEFAULT_WAIT_FOR_ENGINE_MAX_RETRY_COUNT = 500; + /** + * The constant MAX_POOL_SIZE. + * + * @since 1.0.4 + */ + public static final int MAX_POOL_SIZE = 4096; /** * The constant DEFAULT_WAIT_FOR_ENGINE_SHEEP_INTERVAL_MILLIS. * @@ -104,6 +110,7 @@ public final class JavetEngineConfig { private String globalName; private IJavetLogger javetLogger; private JSRuntimeType jsRuntimeType; + private int observerTimeoutMillis; private int poolDaemonCheckIntervalMillis; private int poolIdleTimeoutSeconds; private int poolMaxSize; @@ -193,6 +200,16 @@ public IJavetLogger getJavetLogger() { return javetLogger; } + /** + * Gets observer timeout millis. + * + * @return the observer timeout millis + * @since 3.1.4 + */ + public int getObserverTimeoutMillis() { + return observerTimeoutMillis; + } + /** * Gets pool daemon check interval millis. * @@ -332,6 +349,7 @@ public JavetEngineConfig setAllowEval(boolean allowEval) { * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setAutoSendGCNotification(boolean autoSendGCNotification) { this.autoSendGCNotification = autoSendGCNotification; return this; @@ -344,6 +362,7 @@ public JavetEngineConfig setAutoSendGCNotification(boolean autoSendGCNotificatio * @return the self * @since 0.7.2 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setDefaultEngineGuardTimeoutMillis(int defaultEngineGuardTimeoutMillis) { assert defaultEngineGuardTimeoutMillis > 0 : "The default engine guard timeout millis must be greater than 0."; this.defaultEngineGuardTimeoutMillis = defaultEngineGuardTimeoutMillis; @@ -357,6 +376,7 @@ public JavetEngineConfig setDefaultEngineGuardTimeoutMillis(int defaultEngineGua * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setGCBeforeEngineClose(boolean gcBeforeEngineClose) { this.gcBeforeEngineClose = gcBeforeEngineClose; return this; @@ -369,6 +389,7 @@ public JavetEngineConfig setGCBeforeEngineClose(boolean gcBeforeEngineClose) { * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setGlobalName(String globalName) { this.globalName = globalName; return this; @@ -381,6 +402,7 @@ public JavetEngineConfig setGlobalName(String globalName) { * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setJSRuntimeType(JSRuntimeType jsRuntimeType) { this.jsRuntimeType = Objects.requireNonNull(jsRuntimeType); return this; @@ -393,11 +415,26 @@ public JavetEngineConfig setJSRuntimeType(JSRuntimeType jsRuntimeType) { * @return the self * @since 0.7.0 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setJavetLogger(IJavetLogger javetLogger) { this.javetLogger = Objects.requireNonNull(javetLogger); return this; } + /** + * Sets observer timeout millis. + * + * @param observerTimeoutMillis the observer timeout millis + * @return the self + * @since 3.1.4 + */ + @SuppressWarnings("UnusedReturnValue") + public JavetEngineConfig setObserverTimeoutMillis(int observerTimeoutMillis) { + assert observerTimeoutMillis > 0 : "The observer timeout millis must be greater than 0."; + this.observerTimeoutMillis = observerTimeoutMillis; + return this; + } + /** * Sets pool daemon check interval millis. * @@ -419,6 +456,7 @@ public JavetEngineConfig setPoolDaemonCheckIntervalMillis(int poolDaemonCheckInt * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setPoolIdleTimeoutSeconds(int poolIdleTimeoutSeconds) { assert poolIdleTimeoutSeconds > 0 : "The pool idle timeout seconds must be greater than 0."; this.poolIdleTimeoutSeconds = poolIdleTimeoutSeconds; @@ -432,6 +470,7 @@ public JavetEngineConfig setPoolIdleTimeoutSeconds(int poolIdleTimeoutSeconds) { * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setPoolMaxSize(int poolMaxSize) { assert poolMaxSize > 0 : "Pool max size must be greater than 0."; assert poolMaxSize <= MAX_POOL_SIZE : "Pool max size must be no greater than " + MAX_POOL_SIZE + "."; @@ -448,6 +487,7 @@ public JavetEngineConfig setPoolMaxSize(int poolMaxSize) { * @return the self * @since 0.7.0 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setPoolMinSize(int poolMinSize) { assert poolMinSize > 0 : "Pool min size must be greater than 0."; assert poolMinSize <= MAX_POOL_SIZE : "Pool min size must be no greater than " + MAX_POOL_SIZE + "."; @@ -464,6 +504,7 @@ public JavetEngineConfig setPoolMinSize(int poolMinSize) { * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setPoolShutdownTimeoutSeconds(int poolShutdownTimeoutSeconds) { assert poolShutdownTimeoutSeconds > 0 : "The pool shutdown timeout seconds must be greater than 0."; this.poolShutdownTimeoutSeconds = poolShutdownTimeoutSeconds; @@ -477,6 +518,7 @@ public JavetEngineConfig setPoolShutdownTimeoutSeconds(int poolShutdownTimeoutSe * @return the self * @since 0.9.1 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setResetEngineTimeoutSeconds(int resetEngineTimeoutSeconds) { assert resetEngineTimeoutSeconds > 0 : "The reset engine timeout seconds must be greater than 0."; this.resetEngineTimeoutSeconds = resetEngineTimeoutSeconds; @@ -490,7 +532,9 @@ public JavetEngineConfig setResetEngineTimeoutSeconds(int resetEngineTimeoutSeco * @return the self * @since 1.0.5 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setWaitForEngineLogIntervalMillis(int waitForEngineLogIntervalMillis) { + assert waitForEngineLogIntervalMillis > 0 : "The wait for engine log interval millis must be greater than 0."; this.waitForEngineLogIntervalMillis = waitForEngineLogIntervalMillis; return this; } @@ -502,7 +546,9 @@ public JavetEngineConfig setWaitForEngineLogIntervalMillis(int waitForEngineLogI * @return the self * @since 1.1.6 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setWaitForEngineMaxRetryCount(int waitForEngineMaxRetryCount) { + assert waitForEngineMaxRetryCount >= 0 : "The wait for engine max retry count must be no less than 0."; this.waitForEngineMaxRetryCount = waitForEngineMaxRetryCount; return this; } @@ -514,6 +560,7 @@ public JavetEngineConfig setWaitForEngineMaxRetryCount(int waitForEngineMaxRetry * @return the self * @since 1.0.5 */ + @SuppressWarnings("UnusedReturnValue") public JavetEngineConfig setWaitForEngineSleepIntervalMillis(int[] waitForEngineSleepIntervalMillis) { Objects.requireNonNull(waitForEngineSleepIntervalMillis); assert waitForEngineSleepIntervalMillis.length > 0; diff --git a/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapSpaceStatistics.java b/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapSpaceStatistics.java index a139d4366..345fe17c5 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapSpaceStatistics.java +++ b/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapSpaceStatistics.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; /** * The type V8 runtime observer average V8 heap space statistics. @@ -32,6 +33,24 @@ * @since 1.0.5 */ public class V8RuntimeObserverAverageV8HeapSpaceStatistics implements IV8RuntimeObserver { + /** + * The constant DEFAULT_CAPACITY. + * + * @since 3.1.4 + */ + protected static final int DEFAULT_CAPACITY = 256; + /** + * The constant DEFAULT_TIMEOUT_MILLIS. + * + * @since 3.1.4 + */ + protected static final int DEFAULT_TIMEOUT_MILLIS = 5000; + /** + * The Timeout millis. + * + * @since 3.1.4 + */ + protected final int timeoutMillis; /** * The V8 allocation space. * @@ -52,7 +71,7 @@ public class V8RuntimeObserverAverageV8HeapSpaceStatistics implements IV8Runtime * @since 1.0.5 */ public V8RuntimeObserverAverageV8HeapSpaceStatistics(V8AllocationSpace v8AllocationSpace) { - this(v8AllocationSpace, 256); + this(v8AllocationSpace, DEFAULT_CAPACITY, DEFAULT_TIMEOUT_MILLIS); } /** @@ -60,10 +79,15 @@ public V8RuntimeObserverAverageV8HeapSpaceStatistics(V8AllocationSpace v8Allocat * * @param v8AllocationSpace the V8 allocation space * @param capacity the capacity + * @param timeoutMillis the timeout millis * @since 1.0.6 */ - public V8RuntimeObserverAverageV8HeapSpaceStatistics(V8AllocationSpace v8AllocationSpace, int capacity) { + public V8RuntimeObserverAverageV8HeapSpaceStatistics( + V8AllocationSpace v8AllocationSpace, + int capacity, + int timeoutMillis) { this.v8AllocationSpace = Objects.requireNonNull(v8AllocationSpace); + this.timeoutMillis = timeoutMillis; v8HeapSpaceStatisticsFutureList = new ArrayList<>(capacity); } @@ -75,24 +99,33 @@ public V8HeapSpaceStatistics getResult() { long spaceSize = 0; long spaceUsedSize = 0; if (!v8HeapSpaceStatisticsFutureList.isEmpty()) { + int count = 0; + final long expectedEndTime = System.currentTimeMillis() + timeoutMillis; for (CompletableFuture v8HeapSpaceStatisticsFuture : v8HeapSpaceStatisticsFutureList) { try { - V8HeapSpaceStatistics v8HeapSpaceStatistics = v8HeapSpaceStatisticsFuture.join(); - if (spaceName.isEmpty()) { - spaceName = v8HeapSpaceStatistics.getSpaceName(); + final long now = System.currentTimeMillis(); + V8HeapSpaceStatistics v8HeapSpaceStatistics = now < expectedEndTime + ? v8HeapSpaceStatisticsFuture.get(expectedEndTime - now, TimeUnit.MILLISECONDS) + : v8HeapSpaceStatisticsFuture.getNow(null); + if (v8HeapSpaceStatistics != null) { + if (spaceName.isEmpty()) { + spaceName = v8HeapSpaceStatistics.getSpaceName(); + } + physicalSpaceSize += v8HeapSpaceStatistics.getPhysicalSpaceSize(); + spaceAvailableSize += v8HeapSpaceStatistics.getSpaceAvailableSize(); + spaceSize += v8HeapSpaceStatistics.getSpaceSize(); + spaceUsedSize += v8HeapSpaceStatistics.getSpaceUsedSize(); + ++count; } - physicalSpaceSize += v8HeapSpaceStatistics.getPhysicalSpaceSize(); - spaceAvailableSize += v8HeapSpaceStatistics.getSpaceAvailableSize(); - spaceSize += v8HeapSpaceStatistics.getSpaceSize(); - spaceUsedSize += v8HeapSpaceStatistics.getSpaceUsedSize(); } catch (Throwable ignored) { } } - final int v8RuntimeCount = v8HeapSpaceStatisticsFutureList.size(); - physicalSpaceSize = physicalSpaceSize / v8RuntimeCount; - spaceAvailableSize = spaceAvailableSize / v8RuntimeCount; - spaceSize = spaceSize / v8RuntimeCount; - spaceUsedSize = spaceUsedSize / v8RuntimeCount; + if (count > 0) { + physicalSpaceSize = physicalSpaceSize / count; + spaceAvailableSize = spaceAvailableSize / count; + spaceSize = spaceSize / count; + spaceUsedSize = spaceUsedSize / count; + } } return new V8HeapSpaceStatistics( spaceName, diff --git a/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapStatistics.java b/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapStatistics.java index d63ea6f9b..c0df80e95 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapStatistics.java +++ b/src/main/java/com/caoccao/javet/interop/engine/observers/V8RuntimeObserverAverageV8HeapStatistics.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; /** * The type V8 runtime observer average V8 heap statistics. @@ -29,6 +30,24 @@ * @since 1.0.5 */ public class V8RuntimeObserverAverageV8HeapStatistics implements IV8RuntimeObserver { + /** + * The constant DEFAULT_CAPACITY. + * + * @since 3.1.4 + */ + protected static final int DEFAULT_CAPACITY = 256; + /** + * The constant DEFAULT_TIMEOUT_MILLIS. + * + * @since 3.1.4 + */ + protected static final int DEFAULT_TIMEOUT_MILLIS = 5000; + /** + * The Timeout millis. + * + * @since 3.1.4 + */ + protected final int timeoutMillis; /** * The V8 heap statistics future list. * @@ -42,16 +61,18 @@ public class V8RuntimeObserverAverageV8HeapStatistics implements IV8RuntimeObser * @since 1.0.5 */ public V8RuntimeObserverAverageV8HeapStatistics() { - this(256); + this(DEFAULT_CAPACITY, DEFAULT_TIMEOUT_MILLIS); } /** * Instantiates a new V8 runtime observer for average V8 heap statistics. * - * @param capacity the capacity + * @param capacity the capacity + * @param timeoutMillis the timeout millis * @since 1.0.6 */ - public V8RuntimeObserverAverageV8HeapStatistics(int capacity) { + public V8RuntimeObserverAverageV8HeapStatistics(int capacity, int timeoutMillis) { + this.timeoutMillis = timeoutMillis; v8HeapStatisticsFutureList = new ArrayList<>(capacity); } @@ -72,41 +93,50 @@ public V8HeapStatistics getResult() { long usedGlobalHandlesSize = 0; long usedHeapSize = 0; if (!v8HeapStatisticsFutureList.isEmpty()) { + int count = 0; + final long expectedEndTime = System.currentTimeMillis() + timeoutMillis; for (CompletableFuture v8HeapStatisticsFuture : v8HeapStatisticsFutureList) { try { - V8HeapStatistics v8HeapStatistics = v8HeapStatisticsFuture.join(); - doesZapGarbage += v8HeapStatistics.getDoesZapGarbage(); - externalMemory += v8HeapStatistics.getExternalMemory(); - heapSizeLimit += v8HeapStatistics.getHeapSizeLimit(); - mallocedMemory += v8HeapStatistics.getMallocedMemory(); - numberOfDetachedContexts += v8HeapStatistics.getNumberOfDetachedContexts(); - numberOfNativeContexts += v8HeapStatistics.getNumberOfNativeContexts(); - peakMallocedMemory += v8HeapStatistics.getPeakMallocedMemory(); - totalAvailableSize += v8HeapStatistics.getTotalAvailableSize(); - totalGlobalHandlesSize += v8HeapStatistics.getTotalGlobalHandlesSize(); - totalHeapSize += v8HeapStatistics.getTotalHeapSize(); - totalHeapSizeExecutable += v8HeapStatistics.getTotalHeapSizeExecutable(); - totalPhysicalSize += v8HeapStatistics.getTotalPhysicalSize(); - usedGlobalHandlesSize += v8HeapStatistics.getUsedGlobalHandlesSize(); - usedHeapSize += v8HeapStatistics.getUsedHeapSize(); + final long now = System.currentTimeMillis(); + V8HeapStatistics v8HeapStatistics = now < expectedEndTime + ? v8HeapStatisticsFuture.get(expectedEndTime - now, TimeUnit.MILLISECONDS) + : v8HeapStatisticsFuture.getNow(null); + if (v8HeapStatistics != null) { + doesZapGarbage += v8HeapStatistics.getDoesZapGarbage(); + externalMemory += v8HeapStatistics.getExternalMemory(); + heapSizeLimit += v8HeapStatistics.getHeapSizeLimit(); + mallocedMemory += v8HeapStatistics.getMallocedMemory(); + numberOfDetachedContexts += v8HeapStatistics.getNumberOfDetachedContexts(); + numberOfNativeContexts += v8HeapStatistics.getNumberOfNativeContexts(); + peakMallocedMemory += v8HeapStatistics.getPeakMallocedMemory(); + totalAvailableSize += v8HeapStatistics.getTotalAvailableSize(); + totalGlobalHandlesSize += v8HeapStatistics.getTotalGlobalHandlesSize(); + totalHeapSize += v8HeapStatistics.getTotalHeapSize(); + totalHeapSizeExecutable += v8HeapStatistics.getTotalHeapSizeExecutable(); + totalPhysicalSize += v8HeapStatistics.getTotalPhysicalSize(); + usedGlobalHandlesSize += v8HeapStatistics.getUsedGlobalHandlesSize(); + usedHeapSize += v8HeapStatistics.getUsedHeapSize(); + ++count; + } } catch (Throwable ignored) { } } - final int v8RuntimeCount = v8HeapStatisticsFutureList.size(); - doesZapGarbage = doesZapGarbage / v8RuntimeCount; - externalMemory = externalMemory / v8RuntimeCount; - heapSizeLimit = heapSizeLimit / v8RuntimeCount; - mallocedMemory = mallocedMemory / v8RuntimeCount; - numberOfDetachedContexts = numberOfDetachedContexts / v8RuntimeCount; - numberOfNativeContexts = numberOfNativeContexts / v8RuntimeCount; - peakMallocedMemory = peakMallocedMemory / v8RuntimeCount; - totalAvailableSize = totalAvailableSize / v8RuntimeCount; - totalGlobalHandlesSize = totalGlobalHandlesSize / v8RuntimeCount; - totalHeapSize = totalHeapSize / v8RuntimeCount; - totalHeapSizeExecutable = totalHeapSizeExecutable / v8RuntimeCount; - totalPhysicalSize = totalPhysicalSize / v8RuntimeCount; - usedGlobalHandlesSize = usedGlobalHandlesSize / v8RuntimeCount; - usedHeapSize = usedHeapSize / v8RuntimeCount; + if (count > 0) { + doesZapGarbage = doesZapGarbage / count; + externalMemory = externalMemory / count; + heapSizeLimit = heapSizeLimit / count; + mallocedMemory = mallocedMemory / count; + numberOfDetachedContexts = numberOfDetachedContexts / count; + numberOfNativeContexts = numberOfNativeContexts / count; + peakMallocedMemory = peakMallocedMemory / count; + totalAvailableSize = totalAvailableSize / count; + totalGlobalHandlesSize = totalGlobalHandlesSize / count; + totalHeapSize = totalHeapSize / count; + totalHeapSizeExecutable = totalHeapSizeExecutable / count; + totalPhysicalSize = totalPhysicalSize / count; + usedGlobalHandlesSize = usedGlobalHandlesSize / count; + usedHeapSize = usedHeapSize / count; + } } return new V8HeapStatistics( doesZapGarbage,