This project is read-only.

Difficulty Understanding GC in V8.Net

Nov 18, 2013 at 7:45 AM
Hey James,

I'm having a lot of trouble understanding garbage collection. Once I am finished with this framework I need to end everything, basically clean up on all possible finalizers. I am attempting to use this as a DLL in another project that is based on Mono and forces a reload of all Assemblies and when the V8.Net assembly is unloaded I am getting a deadlock on finalizers.

The only way I can get it to currently work is to actually disable the worker thread altogether but now I am assuming that nothing is being properly disposed of and stop the Isolate from being disposed of on the native side which is obviously not what I want.

Is there any advice you can give me on another way I can avoid the thread for garbage cleanup and just do it forcefully myself?
Nov 18, 2013 at 3:05 PM
Edited Nov 18, 2013 at 3:09 PM
The disposal process works as described in this post: https://v8dotnet.codeplex.com/discussions/467935

Basically to understand it you must understand the dilemma: The managed side cannot allow garbage collection (finalization) of objects unless there are no more script references, and likewise, the native V8 engine must not dispose of the last handle if there's a managed object relying on it (external references outside its control).

How to solve this? Simple:
  1. Block the managed finalizer, and make the V8 native handle "weak" (for the V8 GC). This, however, is not possible in the finalizer thread, because the main thread might be executing script, so instead it is deferred to the worker thread to "deal with" later.
Now, the ball is in the V8 engine's court. There's a hook that causes V8.Net to be notified when the native V8 GC wants to collect a weak native handle, and in such case:
  1. The native V8 GC calls back when ready to dispose of a handle. The object is then allowed to be finalized by the managed GC.
  2. The managed GC tries (again) to finalize the object (since it was re-registered), which then succeeds.
This is the full cycle that occurs when you type "\gctest" in the test console app (which works, but might need a bit more testing).

So, you see, there's a tricky balance to make this work.

What I can do is look into a method that can be called to forcibly dispose the engine itself - but for now, you can try to simply null any references to the engine and force a managed GC to collect and wait for finalizers. The native side should be disposed once V8Engine engine is collected; Let me know if this causes any issues as well - typing "\exit" in the console app seems to dispose of the engine just fine (naturally by the GC).
Nov 18, 2013 at 3:11 PM
BTW: There's a new update to the binaries in the dev branch that has resolved some issues with dead locking of the threads. Have you tried those yet?
Nov 18, 2013 at 9:03 PM
I can't deal with the binaries as they are. Have you updated the source in the dev branch as well? I can only test it from source. What is the issue with deadlocking threads you fixed?
Nov 18, 2013 at 9:05 PM
I still think its a good idea to give the option of disposing of objects as a developer see's fit instantly from managed to native. I understand that references to them can make that difficult but as long as the developer knows what they are doing I don't see this being a problem?
Nov 18, 2013 at 9:05 PM
Yes, the dev source is updated as well. The issue was I was using a "lock(){}" everywhere instead of utilizing a better method: ReaderWriterLock
Nov 18, 2013 at 9:10 PM
Edited Nov 18, 2013 at 9:10 PM
Disposing of the entire engine is easy to put in - but other than that, you cannot force disposal of objects in the current context, as the native V8 engine has the final say. That said, it should be possible to destroy the V8 engine's context object and create a new one (and clear out all the current managed objects also) - will have to look into it.
Nov 18, 2013 at 9:13 PM
jamesnw wrote:
What I can do is look into a method that can be called to forcibly dispose the engine itself - but for now, you can try to simply null any references to the engine and force a managed GC to collect and wait for finalizers. The native side should be disposed once V8Engine engine is collected; Let me know if this causes any issues as well - typing "\exit" in the console app seems to dispose of the engine just fine (naturally by the GC).
I would like to try this the only problem is with how I am using this there is no guarantee that the V8Engine object will be collected because there is no way of actually exiting the application context and I can't force the Assembly to be unloaded myself. I have to consider the fact that it's constantly running and being unloaded/reloaded as an Assembly which is outside of my control.

Can you possibly help with some tips on how I can forcibly dispose of everything?
Nov 18, 2013 at 9:58 PM
James,

The new Handles~.cs source file seems interesting. Is this intentionally named as such ?
Nov 18, 2013 at 9:59 PM
That's an old file, just ignore it. ;) '~' to me just means "delete eventually".
Nov 18, 2013 at 10:00 PM
I'm working on something you can use.
Nov 18, 2013 at 10:26 PM
So I've updated to your latest source and I am still getting some sort of deadlock in finalizers. Also the destructor is never being called which tells me that V8Engine object is not being collected correctly.

I think the only way to use V8.Net in the reference I am using it in is to avoid the background thread and find another way to dispose of objects
Nov 18, 2013 at 10:31 PM
I know what this issue is I think, which will be fixed shortly...
Nov 18, 2013 at 10:33 PM
I could be trying to destroy V8Engine incorrectly.

I am a low level engineer who normally works in C/C++ so C# is still kind of new to me I am just making null and calling GC to collect
Nov 18, 2013 at 11:27 PM
You need two things:
GC.Collect();
GC.WaitForPendingFinalizers();
Nov 18, 2013 at 11:31 PM
Anyhow, never mind that, I have something else for you. I just checked in changes to the dev branch, which now contains a {V8Engine}.Dispose() method - just for you. ;) (ok, anyone who needs it ;) )
Nov 18, 2013 at 11:43 PM
Thanks for this. The funny thing is I've almost come up with the same thing myself lol.

I am not sure of the time difference of where we are but I thought you might of clocked off.

I will try your version, mine is still missing an object which won't allow the isolate on the native side to be disposed of
Nov 19, 2013 at 12:01 AM
I'm in the -5 EST time zone - I'll be around for a few more hours. ;)

Nov 19, 2013 at 12:21 AM
OK your Dispose function and native changes for Dispose works a treat.

Thanks for your help on this
Nov 19, 2013 at 12:43 AM
Awesome! Glad it worked out. :) This helped to identify a few other minor bugs I need to address later on. ;)
Nov 19, 2013 at 12:55 AM
I have one last question which I should probably raise a different topic for.

I have registered a class to be used within V8Engine by just setting the property on the GlobalObject of my already created class which does not extend V8NativeObject and had direct access to everything basically like
SamplePoint p = new SamplePoint();

_engine.GlobalObject.SetProperty("samplePoint", p, null, true, ScriptMemberSecurity.Permanent);
And I have also done it where I extend V8NativeObject and proceed to implement all the setters and getters and accessors for other functions like Print and so on. Why would someone do it this way if its automatically done via the method above without all that extra work?
Nov 19, 2013 at 4:11 AM
Hi,

Remember, the purpose of V8.NET was not to automatically bind your class types. It was to provide a way to interface with the V8 engine from the managed side (and support Mono). Once that was achieved (for the most part), I decided to write in some support to aid in binding non-V8NativeObject class types - but that may not be as fast as a custom implementation. I have to handle "any and all cases" (one size fits all), but a class that needs more performance might be able to work faster using certain logistical shortcuts within custom callback methods (for instance, I have to validate and convert types, but a custom callback may be more precise, only caring about one type, or you may wish to define special behaviour when "new ..." is used on a function [which is not allowed on bound class methods]). It boils down to this comparison: You get more power out of C++, but you can work faster in C# (most of the time), with the former usually faster than the latter for lower level operations. ;) With more power comes more ... well, you know. ;)
Dec 15, 2013 at 1:21 PM
Hey James -

Could you make a release that includes the Dispose() method on V8Engine? I'm using V8.Net as part of some integration tests and the test runner doesn't shut down because the Worker Thread doesn't ever quit (or at least, that's what I figure from looking through the source). All Handle's are disposed from what I can see in my code.

By far, this is the best V8 wrapper for .NET that I've seen by the way! Thanks for this!

Tim
Dec 15, 2013 at 1:35 PM
It already exists in the dev branch. :) (source and binaries)

Dec 16, 2013 at 8:19 AM
Ah, yeah - it would help if I selected the right branch in CodePlex...

Seems to be making no difference though James. All I'm doing at the moment is creating a new V8Engine on the start of the test, and then disposing of it on test end. It's actually worse though, because I get an OutOfMemoryException if I call Dispose(). If I don't call Dispose(), then the test runner never returns.
Dec 16, 2013 at 2:49 PM
Can you zip and send the source? (or post an example of the code you're running). I'll give it a test run also.
Dec 16, 2013 at 8:27 PM
Hi James -

A link to a project can be found at: http://sdrv.ms/1heNkwQ

It's a new Test Project (VS 2012), with a single test class and a single test method. It just creates a new V8Engine object. When you run the test, the test runner never stops, and if you call engine.Dispose(), you get the OutOfMemoryException.

Thanks.
  • Tim
Dec 16, 2013 at 9:32 PM
I just ran it fine, with no errors (with and without the disposal line). I think your memory is just too low.

I do not recommend using unit tests to test engine creation and disposal like this anyhow. You should use the Visual Studio unit tests for MANAGED code, not native code controlled by managed code. I did try do put them in, but 1. It is not cross-platform (for Mono), and 2. The native side does not get unloaded after a test - re-compiling the source code fails because the native DLL becomes locked after a test. I ended up writing all my own "unit tests" in JavaScript instead, and I use a console command I have setup to make sure everything works before a release.