Javet icon indicating copy to clipboard operation
Javet copied to clipboard

Getting HeapStatistics from another thread

Open user1545 opened this issue 1 year ago • 14 comments

Getting HeapStatistics from another thread seems to crash the jvm occasionally. I have built a simplified example, heap settings are set a bit higher so that the crash occurs more often, but the same problem exists with a smaller heap and smaller array, just not as often...

Without HeapStatistic (activeMonitoring=false) this test case runs steadily 100% every time. If I activate call HeapStatistics from another thread (activeMonitioring=true) however, I'll get a jvm crash every 2-3 times.

I think to remember that you have warned somewhere for exposing a v8Runtime to other threads, so that's probably the root cause here, but that would also mean that I can't build an effective monitoring because if I can't leave my local thread I can only monitor before and after an execution, but not the execution itself???

Or am I missing something here? Please assist...

        final boolean activeMonitoring = true;	
		
	V8RuntimeOptions.V8_FLAGS.setMaxHeapSize(8096);
	V8RuntimeOptions.V8_FLAGS.setMaxOldSpaceSize(8096);

	try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) {
		final ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();

		try {
			if (activeMonitoring) {
				timerService.scheduleAtFixedRate(() -> {

					V8HeapStatistics stat = v8Runtime.getV8HeapStatistics();
					double a = stat.getUsedHeapSize();
					double b = stat.getHeapSizeLimit();
					double c = a / b;
					System.out.println("Thread-ID: " + Thread.currentThread().getId() + " "
							+ stat.getHeapSizeLimit() + " " + stat.getUsedHeapSize() + " " + c);
				}, 250, 1000, TimeUnit.MILLISECONDS);
			}
			System.out.println(
					v8Runtime.getExecutor("var a = [];for(let i = 0; i < 100000000; i++){ a.push({test:'test'})}")
							.executeString());

		} finally {
			timerService.shutdown();
			timerService.awaitTermination(30, TimeUnit.SECONDS);

		}

	}

user1545 avatar Jun 06 '24 08:06 user1545

forgot the output

Thread-ID: 24 8492417024 428501576 0.05045696352275599
Thread-ID: 24 8492417024 1692863712 0.1993382693308491
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffd36d77d78, pid=10008, tid=8752
#
# JRE version: OpenJDK Runtime Environment Corretto-17.0.1.12.1 (17.0.1+12) (build 17.0.1+12-LTS)
# Java VM: OpenJDK 64-Bit Server VM Corretto-17.0.1.12.1 (17.0.1+12-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [libjavet-v8-windows-x86_64.v.3.1.2.dll+0x417d78]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# hs_err_pid10008.log
#
# If you would like to submit a bug report, please visit:
#   https://github.com/corretto/corretto-17/issues/
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

user1545 avatar Jun 06 '24 08:06 user1545

I've reproduced this issue. I think it's a regression bug in V8.

image

image

I've fired a bug to the V8 dev.

caoccao avatar Jun 07 '24 03:06 caoccao

Good news. I removed this check and the crash is gone. This private patch will be included in the new release.

caoccao avatar Jun 07 '24 05:06 caoccao

cool, thanks, great project by the way...

user1545 avatar Jun 07 '24 06:06 user1545

cool, thanks, great project by the way...

Thank you. If possible, could you reply to that thread to get V8 dev team's attention?

caoccao avatar Jun 07 '24 06:06 caoccao

done, even though it says that my message has to be approved first before it can be displayed... But at least the approver should notice...

user1545 avatar Jun 07 '24 07:06 user1545

A new feature request was just created. Please feel free to comment on it.

caoccao avatar Jun 08 '24 02:06 caoccao

I think the v8 team is right GetHeapStatistics() wasn't build to be thread safe... !IsFreeSpaceOrFillerMap(map) might be 1 spilling point, but there is still at least 1 other...

tested with 3.1.3

final boolean activeMonitoring = true;

		try (V8Runtime v8Runtime = V8Host.getV8Instance().createV8Runtime()) {

			final ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();

			try {
				if (activeMonitoring) {
					timerService.scheduleAtFixedRate(() -> {
						V8HeapStatistics stat = v8Runtime.getV8HeapStatistics();

						double a = stat.getUsedHeapSize();
						double b = stat.getHeapSizeLimit();
						double c = a / b;

						System.out.println("Thread-ID: " + Thread.currentThread().getId() + " "
								+ stat.getHeapSizeLimit() + " " + stat.getUsedHeapSize() + " " + c);
					}, 250, 1000, TimeUnit.MILLISECONDS);
				}
				System.out.println(v8Runtime
						.getExecutor(
								"let a = [];while(true){for(let i = 0; i < 1000000; i++){a.push({test:'test'})}a=[];}")
						.executeString());

			} finally {
				timerService.shutdown();
				timerService.awaitTermination(30, TimeUnit.SECONDS);

			}

		}
Thread-ID: 25 1518338048 38814856 0.025564040926938557
Thread-ID: 25 1518338048 117216296 0.07720039430902807
Thread-ID: 25 1518338048 38782048 0.025542433090631474
Thread-ID: 25 1518338048 49139024 0.03236369138264524
Thread-ID: 25 1518338048 52677384 0.03469410785653973
Thread-ID: 25 1518338048 10722968 0.0070623060616340425
Thread-ID: 25 1518338048 98788152 0.06506334483952812
Thread-ID: 25 1518338048 149276792 0.09831591337425274
Thread-ID: 25 1518338048 61477888 0.04049025056111878
Thread-ID: 25 1518338048 37156992 0.024472147061679904
Thread-ID: 25 1518338048 135948656 0.08953780495659422
Thread-ID: 25 1518338048 96691544 0.06368248765639838
Thread-ID: 25 1518338048 92498224 0.0609207047941935
Thread-ID: 25 1518338048 7481856 0.0049276615374654695
Thread-ID: 25 1518338048 66629936 0.043883465930243226
Thread-ID: 25 1518338048 55682744 0.03667348261037584
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffd22b217e8, pid=28520, tid=17184
#
# JRE version: OpenJDK Runtime Environment Corretto-17.0.1.12.1 (17.0.1+12) (build 17.0.1+12-LTS)
# Java VM: OpenJDK 64-Bit Server VM Corretto-17.0.1.12.1 (17.0.1+12-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [libjavet-v8-windows-x86_64.v.3.1.3.dll+0x4217e8]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# hs_err_pid28520.log
#
# If you would like to submit a bug report, please visit:
#   https://github.com/corretto/corretto-17/issues/
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

I think we'll have to wait and see what the development team decides regarding your feature request.

user1545 avatar Jun 10 '24 08:06 user1545

Interesting, it doesn't break in my env (also Windows 10).

caoccao avatar Jun 10 '24 10:06 caoccao

Ubuntu 23.10 hs_err_pid5337.log

Interestingly, there seems to be a way to send the HeapStatistics to the calling thread. At least ClearScript (C# v8binding lib) claims that...

"..As long as the engine is busy, ClearScript periodically schedules a callback on the script execution thread. The callback checks V8's heap size..." https://github.com/Microsoft/ClearScript/issues/84

It's implemented in ClearScripts MaxHeapSize setting ... https://microsoft.github.io/ClearScript/Reference/html/P_Microsoft_ClearScript_V8_V8Runtime_MaxHeapSize.htm

Which is working impeccable and throws an c# exception before a v8 OOM can occur.

Its not the same as a monitoring method but it's kind of a Proof of concept, that there must be a way to gain threadsafe access.

On the other hand, GetHeapInfo() does not work, it seems to implement v8locker and does not start at all in parallel to a running execution.

user1545 avatar Jun 10 '24 13:06 user1545

I had the similar idea.

  • Get the heap statistics after every call.
  • It downgrades the performance.
  • It doesn't completely resolve the issue.

The fundamental fix is patching V8 source code. But, that's a considerable amount of work. Would anyone pay me to do so?

caoccao avatar Jun 10 '24 13:06 caoccao

switching to mail

user1545 avatar Jun 10 '24 14:06 user1545

My test shows v8::Isolate::RequestInterrupt() works. There is still quite a lot of work to do. Please check my email reply out and let me know if you have questions.

caoccao avatar Jun 11 '24 04:06 caoccao

The memory leak issue has been fixed. I look forward to your test results.

caoccao avatar Jun 27 '24 03:06 caoccao