I never got a reply on where I left this discussion a couple weeks ago so I’m bringing it up again. I have run some tests with WireBox providers inside of ColdBox while introspecting the contents of my Java heap with jRockit Mission Control Memleak Tester as well as digging through a heap dump of my JVM in the Ecliple Memory Analyzer Tool. I beleive I have confirmed my suspicion below that the current implementation of providers and provider methods (which keeps a hard reference to the injector that spawned it) can in fact widen the scope of the ENTIRE ColdBox framework and everything in it thus creating a memory leak. The scenario can be easily produced with a component that injects a provider with a property like so:
I placed the property above into my user object which is fitting since my user object is normally persisted in both application and session scopes for various uses.
Then I set up a simple test handler with the following code that creates an instance of a user object (with its provider object injected by WireBox) and places a reference to that object in the application scope.
<cfset user = getPlugin(“beanFactory”).getModel(“user”)>
<cfset application[getTickCount()] = user>
I then called that handler 20 times with ?fwreinit=1 in the URL. Obviously you won’t blatantly reinit that often at once in production, but over the course of several weeks my app is usually reinitted every few days for small hotfixes that get pushed so it’s a realistic example of what might happen in an app over time.
That test created 20 user objects in my application scope, each of which had hard references to a separate provider object which had hard references to the 20 different injectors that created them, which in turn had hard references to the 20 different ColdBox frameworks that existed over the life of my test. I will attempt to attach the screen shot that shows jRockit displaying that I have 20 instances of the ColdBox controller still in memory. The ENTIRE framework is being held in memory 20 times by the providers and not allowed to be garbage collected. This includes all plugins, handlers, interceptors, wirebox mappings… you name it.
Memory-wise, I’m getting lucky right now. According to the Memory analyzer tool, the ColdBox controller only retains 2 MB in the heap for my app (which means I had only leaked 38 Megs), but the foundation of my position is that all those CFC’s shouldn’t have their scope widened in the first place.
I will reiterate my suggestion which is to make the providers an island to themselves with no hard references to the rest of the framework (including the injector). If the providers and components who host provider methods are loaded with the scopeRegistration information they will be able to find the current injector in scope without having any reference to the injector that created them.
Another reason why this would be useful would be so someone could safely call duplicate() on a component if all the dependencies were injected as providers. I don’t have a real-life need for that right now (mostly because I’ve made it a habit to code around that), but it’s been something I always wished I could do.
I can send a pull request of my suggested way of solving providers in a way that does not extend the scope of the injector and all it references if you like.