Ok, here we go. Memory leaks are caused by the following:
* If an object is IDisposable and the object is Transient the Kernel
will track the object in kernel scope. Why?
DisposableStrategy.AfterInitialize overrides all
context.ShouldTrackInstance values and forcibly sets
ShouldTrackInstance = true if context.Instance is IDisposable.
(This would effectively invalidate your
InstancesOfTransientServicesAreNotTracked unit test, if
ObjectWithTransientBehavor had an IDisposable interface)
If you use Ninject.Get<SomeTransientObjectWithDisposableInterface>() N
times, you will end up with N references to N transient objects
with disposable interfaces in Kernel.Tracking.Scope (because
ShouldTrackInstace was forcibly set to true by DisposableStrategy).
If your Kernel is a singleton, well, now you've got a really big a**
memory leak problem on your hands.
------------
Let's take the
ASP.NET perspective now....
System.Web.UI.Page,
Control, and UserControl all have IDisposable.
And you call this in your
asp.net objects:
Kernel.Inject( asp-netObject )
which happens on *every* http request. LOL... now I'm feeling sick.
*stabs self*
This perfectly explains the memory leak issue and why WinDbg had so
many gcroots through Ninject.
Seriously though, am I the only one running Ninject in on a production
system loaded enough to see the dramatic effects caused by this bug???
Load on this app is growing by the day and I need to get this
situation UNDER CONTROL __SOON__. Thank goodness I tested our engine
on a smaller project before I put Ninject in one of our larger
projects. OMG, it would have brought our cluster to its knees and
cost me thousands in support and really pissed off customers.
------------
Anyhow, Nate, I know you're doing all this for free, but can you give
me any guidance on how to deal with this issue?
I'll be more than happy to patch Ninject itself to restore my sanity.
I'm currently using the latest Ninject trunk version. I cannot use
Ninject 1.x release because I had another issue with
ASP.NET that I
posted here:
http://groups.google.com/group/ninject/browse_thread/thread/193f304624f3d4d/7a24f065ba0c9243
Furthermore, I have figured out a way to create bindings OUTSIDE of
the Kernel container, and avoid the use of implicit binding by using:
var targetType = target.GetType();
var context = Kernel.Components.ContextFactory.Create(targetType);
context.Instance = target;
var binding = Kernel.Components.BindingFactory.Create(targetType);
binding.Provider = Kernel.Components.ProviderFactory.Create
(binding.Service);
context.PrepareForActivation(binding);
context.ShouldTrackInstance = false;
Kernel.Inject(target, context);
This works with ImplicitSelfBinding=false and avoids implicit
binding. However, I've found that
DisposableStrategy.AfterInitialization
forces ShouldTrackInstance = true because of the problem i've outlined
above.
I'm thinking what I need to do is completely remove DisposableStrategy
from the kernel and I'll get my memory back (how ironic, lol).
I don't think I can override Dispose() on Pages and Controls because
the impact would be big, so I'm looking towards patching Ninject or
removing DisposableStrategy activation to solve the problem.
Thanks,
Brian
Also, it would be nice to have ManyPerRequest [or TransientPerRequest]
behavior where behavior would be transient within the life type of an
HTTP request, then disposed of *properly* at the end of the an HTTP
request. I have a working patch (assuming we fix this
DisposableStrategy problem) submitted on the bug tracker.
> ASP.NETintegration for Ninject, I was tempted to do this, but I wasn't
> sure that it
> was a good idea to make that decision for users.
>
> Please let me know if you continue to run into trouble. I'll do my best to
> be more responsive next time!
>
> Thanks,
> Nate
>
> ...
>
> read more »