Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

out of memory when repeatedly initializing Clearscript V8 engine (GC issue?)

I have created a basic, default ASP.NET 5 project. I have a controller that creates

var engine = new V8ScriptEngine();

and returns some mock json. When I refresh page certain amount of times I get

Fatal error in heap setup

Allocation failed - process out of memory

And following stack trace

Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at V8Isolate.Create(StdString* , V8IsolateConstraints* , Boolean , Int32 )
   at Microsoft.ClearScript.V8.V8IsolateProxyImpl..ctor(String gcName, V8RuntimeConstraints gcConstraints, Boolean enableDebugging, Int32 debugPort)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
   at System.Activator.CreateInstance(Type type, Object[] args)
   at Microsoft.ClearScript.V8.V8Proxy.CreateImpl[T](Object[] args)
   at Microsoft.ClearScript.V8.V8IsolateProxy.Create(String name, V8RuntimeConstraints constraints, Boolean enableDebugging, Int32 debugPort)
   at Microsoft.ClearScript.V8.V8Runtime..ctor(String name, V8RuntimeConstraints constraints, V8RuntimeFlags flags, Int32 debugPort)
   at Microsoft.ClearScript.V8.V8ScriptEngine..ctor(V8Runtime runtime, String name, V8RuntimeConstraints constraints, V8ScriptEngineFlags flags, Int32 debugPort)
   at Microsoft.ClearScript.V8.V8ScriptEngine..ctor()

I tried to look at the memory with dotMemory. Each time I refresh page, an engine is created, and adds like 2MB of ram to unmanaged memory. When it hits certain limit it crashes as explained above. As long as I click force GC before I hit the limit the memory goes down and I can use the page again.

My question is: why does GC not handle this in the first place? After each request the object can be disposed, if I force GC it does. I would asume that if I am almost out of memory but I can reclaim it with GC it would do so.

How can I resolve this? Maybe adding more memory would help but I don't know how to do this either. If GC will never clean those objects it will break anyway.

Same happens when I run Kestrel (dnx web) and with IIS. I have framework set to "dnx46"

Here is my dnx version

$ dnx --version
Microsoft .NET Execution environment
 Version:      1.0.0-rc1-16231
 Type:         Clr
 Architecture: x86
 OS Name:      Windows
 OS Version:   10.0
 Runtime Id:   win10-x86

ClearScript version is "ClearScript.V8": "5.4.3"

like image 289
Łukasz Avatar asked Feb 07 '23 22:02

Łukasz


1 Answers

The short version: You need to dispose each script engine when you're done with it. A convenient way is to use the using statement:

using (var engine = new V8ScriptEngine()) {
    // do stuff
}

Longer version: Each V8 instance reserves a large block of address space. These don't show up as used memory, but in a 32-bit process you can run out of address space with just a few dozen instances. The managed GC would eventually clean it all up, but because it can't track V8's address space reservations, it's in no hurry to do so, since it doesn't detect any memory pressure. Eventually you get to a point where your memory usage is still low, but V8 can no longer reserve a large-enough block of address space, and so it fails.

like image 130
BitCortex Avatar answered Apr 30 '23 02:04

BitCortex