Recently I had cause to look at speeding up a part of a new application which was creating an array of cfc objects using D/I1 and then populating them from a query. I noted that the time to call getBean() to get a new copy of each cfc was aprox 0.2 seconds which is not terrible except when you are doing it 66 times resulting in a load time of aprox 13 seconds before the beans are even populated with data.
The solution I came up with was to use DI/1 to get a single instance of the bean, then in a loop copy the original and populate the copy. This sped of the load time significantly, however I shortly discovered that the app was consuming a huge amount of memory and eventually would crash.
The problem turned out to be that in my end of years fatigue I had used the "duplicate()" function in my code.
After going back and looking at the documentation again I did a facepalm when I read that the function does a deep copy of the object passed to it.
What that means for those of you who don't know is that every object referenced by that object gets duplicated recursively. Since the object I was duplicating was referencing multiple other singleton objects and each in turn were referencing other singleton objects and eventually the main framework object I was duplicating the entire application multiple times in memory "ouch!".
The solution of course was to use structCopy() instead, which despite its name also copies cfc objects only with structcopy it preserves references instead of duplicating the referenced object.
To demonstrate this I threw together this small test code. All we are doing here is create an instance of a counter component and then passing a reference into another component "main" which we then copy using both duplicate() and structCopy() methods.
I then call the original counter objects increment value and compare the before and after values of the counter when referenced through the different copies on the main component object.
Counter Values Before Increment
Original counter object: #counter.getCount()#
Counter object referenced from main object: #main.getCounter().getCount()#
Counter object referenced from copied main object: #maincopy.getCounter().getCount()#
Counter object referenced from duplicated main object: #mainduplicated.getCounter().getCount()#
Counter Values After Increment
Original counter object: #counter.getCount()#
Counter object referenced from main object: #main.getCounter().getCount()#
Counter object referenced from copied main object: #maincopy.getCounter().getCount()#
Counter object referenced from duplicated main object: #mainduplicated.getCounter().getCount()#
component {
variables.counter = "";
public any function init(counter){
variables.counter = arguments.counter;
return this;
}
public function getCounter(){
return variables.counter;
}
}
component {
variables.counter = 0;
public any function init(){
return this;
}
public void function increment(){
variables.counter += 1;
}
public numeric function getCount(){
return variables.counter;
}
}
As you can see from the below output the counter value when referenced from the duplicated main component does not match the others demonstrating that the counter object it references is not the same one referenced everywhere else now.