I've just realised that when I am programming in Java I avoid local variables like the plague, if I really have to I prefix a member with a lower case "L" and have a method that resets that object (if the object is mine, I usually have a private method called init
or something that the constructor calls)
I've just realised that this is really ugly in so many ways. Am I doing it right?
C++ programmers will know exactly what I mean, locals that don't leave the function's scope are automatically destroyed for us (if it does leave the function scope, use a pointer, blah blah blah)
Pattern for when this happens
I've found that whenever I fit an adapter to a function parameter and interact with it through this adapter is when I use this.
I also tend tohave the adapter maintain a pool of any objects it uses (up to a certain number)
It also occurs when I want to use data-types that require "new" to initialise but only within the method.
The code is a part of some main loop usually, otherwise it wouldn't matter, obviously (it's not a one off thing)
GC Collection amount:
Amount (Mb): | 30| 60| 90| 120| 150| 180| 210| 240| 270| 300| 330| 360|
--------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
With pattern |## (14)
without |################################################################### (350)
The program was pushed through its unit tests the mean GC amount is shown. The standard deviation was less than 5 for both.
This feels like it is somehow related to the flywheel pattern...
There is no code for this because:
It can manifest itself in so many ways! If you have a ComplexNumber
class for example, if you just create them as needed you will generate vast amounts of garbage, if you have any sort of vector
or matrix
classes, same thing.
Another area is anything involving some sort of graph which is carefully traversed to generate another structure, like a scene graph, critical path, even a stack representing the current directory.
Basically if you have "new" to assign to a local variable in a method that gets called a lot you will find this.
Sample used above
It came from a program I wrote to teach people about finite state automata and other state-machines (Markov Chains, so forth), I noticed crippling ram usage and decided to investigate.
Comparison with other languages
Obviously C++ doesn't have this problem. But nor does Python you'll be glad to know. Python's reference counting means (provided you haven't got any cricles) that the moment the method ends things are deleted, infact there is a meta-method for it and you can use it reliably as a destructor (provided you are disciplined enough not to leak it from the method)
I can't be the first to have this problem, looking at similar questions suggests that there is no solution, but I can't believe that this hasn't been encountered before!
About me
I'm coming from C++ and Python (love them both!) to Java, I am "experienced" in Java in that I can read/write stuff that works, it follows a nice design philosophy and such, but I tend to be very mindful of performance and resources. I'm suffering withdrawal from const, I was a total const whore.
How this is not pooling
Suppose you have a GroupElement
class - that represents a member of an algebraic group, we'll use additive notation.
Suppose that g.add(h)
returns a new element, if you do this MANY many times you have a lot of elements. if instead you have:
GroupElement f = new GroupElement(0); //identity
g.add(f,h);
where:
add
's first argument is the place to put the result, we generate no garbage.
The people who don't follow the above
You should know what a complex number is? Suppose a complex number has a method called add
that takes a complex number and returns a new complex number. If you do a=b.add(c);
A LOT of times, you get A LOT minus 1 garbage complex numbers floating around.
If you have inplaceAdd(ComplexNumber target, ComplexNumber value)
say where:
target.real = value.real+real;
target.im = value.im+im;
you create no garbage if you do: b.inplaceAdd(a,c)
- which does the same as the above a=b.add(c)
BTW add
could do this: return new ComplexNumber(real+value.real,im+value.im)
- see what I mean now?
Implementation of example (seriously guys, how do you not get this!)
public class ComplexNumber {
private double real;
private double im;
public ComplexNumber(double r, double i) {
real = r;
im = i;
}
public ComplexNumber add(ComplexNumber value) {
return new ComplexNumber(real + value.real, im + value.im);
}
public void inplaceAdd(ComplexNumber target, ComplexNumber value) {
target.real = real + value.real;
target.im = im + value.im;
}
}