jint: Memory leak in V3?

Hi,

I am currently using v2 without any issues, but I just tried to use 3.0.0-beta-2044 in my software to take advantage at all the improvements. But unfortunately after the change, the software is hogging memory. Clearly it has something to do with v3.

Background The software is my own home automation software where devices have a state determination script (javascript) which gets evaluated every minute or when there’s a change in a datasource it uses (eg a switch).

This is done by looping over the devices and evaluate it’s script (per scenario (day, night, away) it has it’s own functions in the script.

I have written a helper that makes some thing easy by adding helper classes and enums to the context (static functions):

    public static Engine GetEngine()
    {
        var engine = new Engine()
            .SetValue("Log", new Action<string>(p => GetServiceProvider.GetServiceOrCreateInstance<ILogger<JavascriptEvaluator>>().LogInformation(p)))
            .SetValue("Settings", GetServiceProvider.GetServiceOrCreateInstance<Settings>())
            .SetValue("Cache", GetServiceProvider.GetServiceOrCreateInstance<Cacher>());

        engine.SetValue("$", new JavascriptTools(engine));

        // Add all enums
        AppDomain.CurrentDomain.GetAssemblies()
            .Where(a => a.FullName?.StartsWith("MyNamespace") == true)
            .SelectMany(a =>
                a.GetTypes().Where(t => t.IsEnum && t.IsPublic))
            .OrderBy(t => t.Name)
            .ForEach(e => engine.SetValue(e.Name, TypeReference.CreateTypeReference(engine, e)));

        return engine;
    }

And here’s the evaluate function (EvaluateFunction) which gets called multiple times a minute (around 50 times) and another evaluate function that is called by some scheduled tasks.

    public static T Evaluate<T>(string script, object variables = null)
    {
        if (string.IsNullOrEmpty(script)) return default;
        
        var engine = GetEngine();
        PopulateVariables(engine, variables);

        var result = engine.Evaluate(script)
            .ToObject();
        
        engine.Dispose();

        return (T)Convert.ChangeType(result, typeof(T));
    }

    private static void PopulateVariables(Engine engine, object variables)
    {
        if (variables is Dictionary<string, object> dictionary)
            dictionary.ForEach(p => engine.SetValue(p.Key, p.Value));
        else
            variables?.GetType()
                .GetProperties()
                .Where(p => p.GetIndexParameters().Length == 0)
                .ForEach(p => { engine.SetValue(p.Name, p.GetValue(variables)); });
    }

    public static T EvaluateFunction<T>(string script, string function, params object[] parameters)
    {
        if (string.IsNullOrEmpty(script)) return default;

        if (!script.Contains(function)) throw new UndefinedJavascriptFunctionException(function);

        var engine = GetEngine();
        var result = engine
            .Execute(script)
            .Invoke(function, parameters)
            .ToObject();
        
        engine.Dispose();

        return (T)Convert.ChangeType(result, typeof(T));
    }

The GetServiceProvider.GetServiceOrCreateInstance function basically gets the instance of a singleton via DPI. JavascriptTools is a class that adds functions to use in the script, which has subtools for eg. devices and datasources.

Issue Memory keeps increasing over time (hour to two hours it leaks around 400MB) even though disposing the engine. I am surely missing something simple at the moment but can’t quite put my finger on it.

The app is running in a docker container so remote debugging is quite the hassle. But I am certain it has something to do with V3 as V2 is rock stable when it comes to memory usage.

Any ideas?

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 31 (4 by maintainers)

Most upvoted comments

Ditto here, feeling good enough to tinker though. So no worries if it takes time, for now just running V2. How would you like to receive them, should I send you an email so we don’t share it to the world 😌

You can reach me at marko -dot- lahma -at- gmail -dot- com with a link if you would like to share the latest memory dumps.

Correct; I can give it a shot to see if I can create something that behaves the same although there’s a lot done in the project that is being used by the Engine (eg JavascriptTools, DataSourceTools etc). Maybe one of those stay alive or referenced which prevents the Engine from being cleaned up. Although I don’t understand why it’s all fine and dandy with V2.

V3 is very different, so it could be anything - Jint bug, your code, anything. There’s been a lot of things moving and you should get better performance and features - and unfortunately some growing / teething pains. V3 should offer superior performance, features etc, but might have issues.

As it’s not thread safe, isn’t creating a Realm also going to cause issues?

Realm is not thread safe (it’s the same Engine instance), just a suggestion if you would pool engines in thread-safe manner.

It might be easier for you to use dotnet-dump and/or dotnet-gcdump directly in docker container. You can then analyze the dump file directly in the container, or copy it out to a Windows machine and open it with Visual Studio.

Closing this as I’ve been unable to repro locally even with million engines created in a loop and disposed using object wrappers, must be something specific to this setup.

Your help has been much appreciated. Still trying to figure out what’s specific about my setup; but for now I can finally use v3!

Keep up the good work!

Closing this as I’ve been unable to repro locally even with million engines created in a loop and disposed using object wrappers, must be something specific to this setup.

None of the bits that touches the MQTTnet implementation (basically changed a lot from non DI classes to singleton classes). I see plenty of issues there that match mine (including one that has a fix 6 days ago but not yet released) so I call that unrelated to jint 😀

The jint issues I was having have completely been resolved with that minor change, so happy with that!

Update @lahma: it seems to do something, memory only risen from 145MB to 202MB.

I forgot to build the container with sdk tools so can’t make a dump. So I just recreated the container with sdk tools; will create another dump tonight to see what is causing the 55MB, maybe still some Jint stuff as with V2 it’s rock solid around 145-155MB but we’ll see 😀

I have added it to the code, will let it run again for tonight and check tomorrow 👍🏻