Clearscript: Clearscript holding references to CLR instances

Created on 3 Feb 2019  路  7Comments  路  Source: microsoft/ClearScript

Hi everyone.. I've been using ClearScript for a project where I need to share validation and calculation code between server and client. Overall this has worked very well.

The one challenge has been an ongoing memory leak. I'm calling Javascript functions hosted by ClearScript, passing in some Entity Framework objects. I think ClearScript is holding references to those instances because the ClearScript runtime has no way of knowing if Javascript is holding those references anywhere. That hunch was supported by this profiler trace where ClearScript.HOstItem/HostObject was keeping a strong handle on my entity instance:

image

So I need to work out a way to release these references. So far I can think of a few ideas:

  1. Marshal these entities into simpler types that could be passed by value -- but ultimately I still have complex types so I'd have to use structs here. I'm not sure I can map everything into value types and structs

  2. Create/destroy a new instance of the ClearScript host for each call. -- This would add some overhead because I have to inject my model code with every call.

  3. Use a different hosting model (e.g. out-of-process Node.js I call with the data)

I don't know if there's a easy solution here but I need to resolve this memory leak. Does anyone have experience with this or suggestions on a good approach?

question

All 7 comments

Thinking about this a little more, maybe the best way to handle this is to stringify in incoming and outgoing data in JSON.. that would solve this issue and let me use complex data while keeping ClearScript and staying in process.

Hi @andrejpk,

When you pass a host object to a JavaScript function, the script engine gets a _proxy_ that by necessity holds a strong reference to the host object. That strong reference can be safely released only when the proxy is garbage-collected by the script engine.

The issue here is that V8's garbage collector (you're using V8, right?) heavily favors performance over memory usage, so even if the proxy is unreachable, it may not be collected until V8 detects memory pressure while running another script sometime later.

Therefore, one possibility is to call ScriptEngine.CollectGarbage (passing in true) when convenient. That _should_ release any host objects no longer in use by the script engine, making them available for CLR garbage collection.

Disposing the script engine has the same effect, but it's more expensive as you've noted. Still, it may make sense to do it periodically, perhaps by limiting the number of calls each instance is allowed to process. You can use a combination of techniques to tune your application.

All that having been said, if all you need to do is pass structured data to script code (as opposed to actual "live" host objects), then JSON is a _great_ way to go. You get all the data over in one hop across the slow host-script boundary, all subsequent access is local and fast, and there are no cross-heap concerns at all.

Good luck!

Thanks, this is great information!! I didn't see it in any docs unless I missed something. Would it help if I wordsmithed this into a PR for the docs?

Thanks @andrejpk. We have indeed found that some questions pop up repeatedly, but so far we've just let the information sit in the Issues section, not knowing where else to put it.

We already have an API reference, tutorial, samples, and build/deployment guide. Maybe we should have another document for implementation notes and/or best practices? Another possibility might be to enable GitHub's project-level Wiki.

We'd love to hear your thoughts!

I'll think through the best way.. my immediate thought is a deeper/best practices doc would be a good place for this.

Another question: am I safe in assuming that if I pass a value type like a string into a Javascript function, that holds no references to objects on the CLR side, right?

Another question: am I safe in assuming that if I pass a value type like a string into a Javascript function, that holds no references to objects on the CLR side, right?

Strings, numbers, and bools are passed by value; that is, they're converted to JavaScript values and detached from any managed data.

Other CLR values, including all structs and enums, are boxed and held by reference, although there's some machinery in place to enable "natural" comparison (e.g., x === DayOfWeek.Thursday) for enums and some other values. See ImmutableValueAttribute for more information.

That's the default behavior, and there are ways to override it in specific situations - e.g., if you need to preserve the exact managed type of a numeric value, or provide access to a CLR-specific string method. V8ScriptEngine also supports optional timestamp conversion (see here).

Created Issue #101 to track the best practices documentation. Feel free to reopen this if you have additional questions about the original topic. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

drearyrainDeng picture drearyrainDeng  路  3Comments

roblarky picture roblarky  路  3Comments

yevgeni-zolotko picture yevgeni-zolotko  路  5Comments

Bobris picture Bobris  路  6Comments

fasihrana picture fasihrana  路  8Comments