Quantcast
Channel: ClearScript
Viewing all articles
Browse latest Browse all 2297

New Post: memory leak when passing javascript object to managed code

0
0
I am seeing a memory leak when passing a javascript object to a managed code function. I am using the latest nuget clearscript V8. Please see below for the code that reproduces this issue.

Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;

namespace ClearScriptMemoryLeak
{
    public class Program
    {
        protected V8ScriptEngine m_engine;
        public Program(V8ScriptEngine engine)
        {
            m_engine = engine;
        }

        public void doSomething(dynamic foo)
        {
        }

        public void garbageCollect()
        {
            V8RuntimeHeapInfo beforeInfo = m_engine.GetRuntimeHeapInfo();
            
            m_engine.CollectGarbage(true);
            V8RuntimeHeapInfo afterInfo = m_engine.GetRuntimeHeapInfo();

            Console.WriteLine("garbageCollect: memstats (before/after): used: " + beforeInfo.UsedHeapSize + "/" + afterInfo.UsedHeapSize);
        }

        public static String loadScript(String filePath)
        {
            StringBuilder builder = new StringBuilder();
            FileInfo fileInfo = new FileInfo(filePath);

            using (StreamReader sr = fileInfo.OpenText())
            {
                String s = "";
                while ((s = sr.ReadLine()) != null)
                {
                    builder.AppendLine(s);
                }
            }
            return builder.ToString();
        }

        static void Main(string[] args)
        {
            V8ScriptEngine engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging);
            Program p = new Program(engine);
            
            engine.AddHostObject("nativeBridge", p);

            engine.Execute(loadScript(args[0]));
        }
    }
}
MemLeak.js
var count = 0;
var obj;
while (true) {
    if (++count % 10000 == 0) {
        nativeBridge.garbageCollect();
    }
    obj = {
        a: "b",
        c: "d"
    };
    nativeBridge.doSomething(obj);
}
Running the above javascript will eventually crash with the following exception:
garbageCollect: memstats (before/after): used: 227850008/227529964
garbageCollect: memstats (before/after): used: 228050008/227729964
garbageCollect: memstats (before/after): used: 228250008/227929964
garbageCollect: memstats (before/after): used: 228450008/228129964

Unhandled Exception: Microsoft.ClearScript.ScriptEngineException: Error: Exception of type 'System.OutOfMemoryException' was thrown. ---> System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at Microsoft.ClearScript.HostItem.AdjustInvokeFlags(BindingFlags& invokeFlags)
   at Microsoft.ClearScript.HostItem.InvokeMember(String name, BindingFlags invokeFlags, Object[] args, Object[] bindArgs, CultureInfo culture, Boolean bypassTunneling, Boolean& isCacheable)
   at Microsoft.ClearScript.HostMethod.TryInvoke(IHostInvokeContext context, BindingFlags invokeFlags, Object[] args, Object[] bindArgs, Object& result)
   at Microsoft.ClearScript.Util.InvokeHelpers.TryInvokeObject(IHostInvokeContext context, Object target, BindingFlags invokeFlags, Object[] args, Object[] bindArgs, Boolean tryDynamic, Object& result)
   at Microsoft.ClearScript.HostItem.InvokeHostMember(String name, BindingFlags invokeFlags, Object[] args, Object[] bindArgs, CultureInfo culture, Boolean& isCacheable)
   at Microsoft.ClearScript.HostItem.InvokeMember(String name, BindingFlags invokeFlags, Object[] args, Object[] bindArgs, CultureInfo culture, Boolean bypassTunneling, Boolean& isCacheable)
   at Microsoft.ClearScript.HostItem.<>c__DisplayClass4b.<InvokeReflectMember>b__4a()
   at Microsoft.ClearScript.ScriptEngine.HostInvoke[T](Func`1 func)
   at Microsoft.ClearScript.HostItem.HostInvoke[T](Func`1 func)
   at Microsoft.ClearScript.HostItem.InvokeReflectMember(String name, BindingFlags invokeFlags, Object[] wrappedArgs, CultureInfo culture, String[] namedParams, Boolean& isCacheable)
   at Microsoft.ClearScript.HostItem.System.Reflection.IReflect.InvokeMember(String name, BindingFlags invokeFlags, Binder binder, Object invokeTarget, Object[] wrappedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at Microsoft.ClearScript.HostItem.Microsoft.ClearScript.Util.IDynamic.Invoke(Object[] args, Boolean asConstructor)
   at HostObjectHelpers.Invoke(V8Value* , Void* pvObject, vector<V8Value\,std::allocator<V8Value> >* args, Boolean asConstructor)
   --- End of inner exception stack trace ---
   at V8Exception.ThrowScriptEngineException(V8Exception* )
   at Microsoft.ClearScript.V8.V8ContextProxyImpl.Execute(String gcDocumentName, String gcCode, Boolean evaluate, Boolean discard)
   at Microsoft.ClearScript.V8.V8ScriptEngine.<>c__DisplayClass1b.<Execute>b__19()
   at Microsoft.ClearScript.ScriptEngine.ScriptInvoke[T](Func`1 func)
   at Microsoft.ClearScript.V8.V8ScriptEngine.BaseScriptInvoke[T](Func`1 func)
   at Microsoft.ClearScript.V8.V8ScriptEngine.<>c__DisplayClass25`1.<ScriptInvoke>b__24()
   at Microsoft.ClearScript.V8.?A0x792c8756.InvokeAction(Void* pvActionRef)
   at Microsoft.ClearScript.V8.V8ContextProxyImpl.InvokeWithLock(Action gcAction)
   at Microsoft.ClearScript.V8.V8ScriptEngine.ScriptInvoke[T](Func`1 func)
   at Microsoft.ClearScript.V8.V8ScriptEngine.Execute(String documentName, String code, Boolean evaluate, Boolean discard)
   at Microsoft.ClearScript.ScriptEngine.Execute(String code)
If I remove the 'nativeBridge.doSomething(obj);' line, then there is no memory leak. Likewise, if I move the "obj = {...}" statement outside of the while loop, there is no leak.

Am I doing something wrong with this code? If not, is there any workaround for this issue?

thanks,
Tom

Viewing all articles
Browse latest Browse all 2297