Taritsyn/JavaScriptEngineSwitcher

ClearScript V8 "Cannot pass a GCHandle across AppDomains" System.ArgumentException

xecrets opened this issue · 7 comments

Hi,

I have an application that has been stable several years using ChakraCore. However, I have noticed high memory consumption on a high traffic site, also support for ChakraCore is scheduled to be discontinued soon by Microsoft.

So, I've switched to using ClearScript.V8

JavaScriptEngineSwitcher.Core v3.3.0
JavaScriptEngineSwitcher.V8 v3.8.1
JavaScriptEngineSwitcher.V8.Native.win-x64 v3.8.1

However, I'm now seeing intermittent crashes of the worker process:

An unhandled exception occurred and the process was terminated.

Application ID: /LM/W3SVC/2/ROOT

Process ID: 3416

Exception: System.ArgumentException

Message: Cannot pass a GCHandle across AppDomains.
Parameter name: handle

StackTrace: at System.Runtime.InteropServices.GCHandle.InternalCheckDomain(IntPtr handle)
at System.Runtime.InteropServices.GCHandle.FromIntPtr(IntPtr value)
at Microsoft.ClearScript.V8.SplitProxy.V8SplitProxyManaged.GetAllCachedV8Objects(IntPtr pCache, Ptr pV8ObjectPtrs)
at Microsoft.ClearScript.V8.SplitProxy.V8SplitProxyNative.Impl64.V8Entity_DestroyHandle(Handle hEntity)
at Microsoft.ClearScript.V8.SplitProxy.V8SplitProxyNative.Impl64.Microsoft.ClearScript.V8.SplitProxy.IV8SplitProxyNative.V8Entity_DestroyHandle(Handle hEntity)
at Microsoft.ClearScript.V8.SplitProxy.V8EntityHolder.Destroy(V8EntityHolder& holder)
at Microsoft.ClearScript.V8.SplitProxy.V8ContextProxyImpl.Finalize()

Hello, Svante!

I have an application that has been stable several years using ChakraCore. However, I have noticed high memory consumption on a high traffic site, …

In fact, V8 module consumes more memory than the ChakraCore module. In this case, you only win in speed.

… also support for ChakraCore is scheduled to be discontinued soon by Microsoft.

After March 2021, the ChakraCore library will be maintained by community (see the “ChakraCore Transition” discussion). In fact, it does this already now only in the master branch, there just haven't been any community releases yet.

JavaScriptEngineSwitcher.V8 v3.8.1
JavaScriptEngineSwitcher.V8.Native.win-x64 v3.8.1

Try upgrading to version 3.8.2. If the error persists, then write to ClearScript's authors that you have these problems with version 7.0 RC3.

You can also try upgrading to experimental version of V8 module, which is based on unofficial version of ClearScript library. This version does not contain the code shown in your stack trace.

Thanks for quick feedback.

Actual measurements of memory consumption show that under heavy load ChakraCore ties up huge amounts of unmanaged memory. I've seen up to almost 5GB being tied up. I don't think it's a memory leak as such, as it does not constantly increase, but it is not released either when load is reduced.

V8 will vary between 250K and 400K in private unmanaged bytes, much more manageable.

Anyway, I did just upgrade to 3.8.2, I will monitor and see. If it remains (there's nothing in the change log indicating that anything like this has been fixed) I'll report the issue with the ClearScript project.

Another possibility might be to downgrade to v3.5.5 of the JavaScriptEngineSwitcher.V8, as it appears to be running a stable version 6.0.2 of ClearScript.V8.

I've seen up to almost 5GB being tied up.

Are you using the JavaScript Engine Switcher through some library (ReactJS.NET, Bundle Transformer or something else) or directly?

I'm using ReactJS.NET => React.Core/React.Web/React.Web.Mvc4 v 5.2.9. I'm not running any transforms though, that's done with webpack during the build. It's only used for server side rendering of initial markup. The app is based on Create React App, ejected, with a few minor hacks to handle some requirements not possible to with the vanilla version.

It's unmanaged memory that get's tied up. The managed .NET heap is fine.

I'm running with Configuration.SetReuseJavascriptEngines(true) (I've tried without re-use and it's the same result).

I've also added an inspectionable layer and I can see that IJavaScriptEngineFactory.DisposeEngineForCurrentThread() get's called as expected.

Hello, Svante!

Try upgrading to version 3.8.3.

I've waited a few days for the code to run and restart a number of times, but I'm now happy to report that we've seen no more "Cannot pass a GCHandle across AppDomains" errors since upgrading. Thanks for fast and accurate work!

As for the umanaged memory problems, they appear to be under control although I'm unfortunately not 100% which of the changes we made recently was the key. What we've done is:

  • Switch to ClearScript.V8
  • Explicitly dispose the JavaScriptEnginePool (actually: React.TinyIoC.TinyIoCContainer.Current.Dispose();)
  • Force LOH compaction at some times. GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; We were seeing LOH fragmentation, and apparently large LOH/large committ size may consume some unmanaged memory as well. It's only a stopgap measure, we'll work on the real problem of getting too much into the LOH later.

It may be one or more or all of the above has improved the situation, but I don't have time to isolate the real culprit(s). Later.

Anyway, thanks!

Excellent!