Bug 36431 - embedded mono runtime GC collects object
Summary: embedded mono runtime GC collects object
Status: RESOLVED ANSWERED
Alias: None
Product: Runtime
Classification: Mono
Component: GC ()
Version: 4.0.0
Hardware: Other Linux
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2015-12-01 16:22 UTC by Martin H.
Modified: 2015-12-28 08:37 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Martin H. 2015-12-01 16:22:39 UTC
I have embedded the mono runtime (Mono JIT compiler version 4.0.4 (Stable 4.0.4.1/5ab4c0d Tue Aug 25 18:58:45 EDT 2015) into my C++ application with SGEN garbage collector (libmonosgen-2.0).

The C++ application is running 24/7 as a service on CentOS (CentOS Linux release 7.1.1503 (Core)).

I have 10 instances of the service running on 10 virtual machines hosted in a Dell VMware Cluster.

From time to time one of my services randomly restarts, because of a NullReferenceException in a loaded DLL.
I haven't found a pattern yet, but this may happen once a day in one of my instances.

I have added console output to the destructor of my object in order to see, when it gets garbage collected.
This happens after the instance has been used and the next time it is needed, this causes the NullReferenceException.

The service is calling my C# method once a second (it does some json conversion).
The identical DLL is used in a mono service (not embedded) running on a different virtual machine, also CentOS, on the same hardware, which does not show this behavior (but also not as many instances, just one).

--------------
Therefore my guess is it has someting to do with the embedded mono runtime.
Either I am using it wrong or for whatever reason, the GC gets in some state, where it wants to collect my object.
--------------


As workaround, I have tried to re-instantiate my object, once I got the NullReferenceException. However, if my object was collected once, this happens consistently every second cycle, which leads to the following:

.......
- DoSomeJsonMagic is called
- DoJsonTransformation is called
.......
- DoSomeJsonMagic is called
- MyObject destructor is called    <--------- First time randomly
- throws NullReferenceException
- MyObject is recreated
.......
- DoSomeJsonMagic is called
- DoJsonTransformation is called
.......
- DoSomeJsonMagic is called
- throws NullReferenceException
- MyObject is recreated
.......
- DoSomeJsonMagic is called
- DoJsonTransformation is called
- MyObject destructor is called    <---------- every second cyclce
.......
- DoSomeJsonMagic is called
- throws NullReferenceException
- MyObject is recreated
.......
- DoSomeJsonMagic is called
- DoJsonTransformation is called
- MyObject destructor is called    <---------- every second cyclce
.......
- DoSomeJsonMagic is called
- throws NullReferenceException
- MyObject is recreated
.......

Sometimes, I can see in the logs, that the constructor is called twice!

Bellow you may find a code example, how I use the embedded runtime and some example C# code.

public class InteropClass {
	private readonly MyObject _object;
	
	public InteropClass() {
		_object = new MyObject();
	}
	
	public string DoSomeJsonMagic(string input) {
		try
		{
			Console.WriteLine("DoJsonTransformation");
			var output = _object.DoJsonTransformation(input);
			return output;
		}
		catch (Exception ex) {
			Console.WriteLine($"WARN Recreating Instance \n{ex}\n");
			_object = new MyObject();
			return "";	
		}
	}
}

public class MyObject 
{
	public string DoJsonTransformation(string input) {
		// Some in memory lookup...
		// Some JSON.NET code...
		return input;
	}
	
	~MyObject()
	{
		Console.WriteLine("WARN MyObject destructor called");    
	}
}

MonoClass* interopClass;
MonoObject* interopInstance;

int main(int argc, char *argv[])
{
    qDebug() << "---- Starting Application ----";
    
	MonoDomain* domain = mono_jit_init_version("MyAssembly", "v4.0.30319");
    MonoAssembly* assembly = mono_domain_assembly_open (domain, "MyAssembly.dll");	
    interopClass = mono_class_from_name(image, "MyNamespace", "InteropClass");;
    interopInstance = mono_object_new(domain, interopClass);;
	mono_runtime_object_init(interopInstance);
	
	// run 24/7
	while (true) {
		QString input = QString("my json file");
		// ... some other stuff
		QString output = AnalyzeRawJson(input);
		// wait 1 second
	}	
}

QString AnalyzeRawJson(QString json) const
{
    MonoMethod* method = mono_class_get_method_from_name(interopClass, "DoSomeJsonMagic", 1);
    void* methodArgs [1];
    methodArgs[0] = mono_string_new_wrapper(json.toStdString().c_str());
    MonoObject* result = mono_runtime_invoke(method, interopInstance, methodArgs, NULL);

    auto returnValue = mono_string_to_utf8((MonoString*) result);
    auto converted = QString::fromStdString(returnValue);

    mono_free_method(method);
    mono_free(returnValue);
    return converted;
}
Comment 1 João Matos 2015-12-08 16:16:02 UTC
It's hard to say what could be the problem. You might want to try with a recent version of Mono first, we might have fixed this problem in the meantime.
Comment 2 Rodrigo Kumpera 2015-12-21 01:53:21 UTC
You're storing a naked pointer to a managed object in a static variable. That can't possibly work. 

Try using GC handles and see if it fixes your issues.
Comment 3 Martin H. 2015-12-28 08:37:59 UTC
A call to mono_gchandle_new(interopInstance, true); solved the issue.

Thanks.