I have a Java game development framework. Certain classes, such as Texture
, need to be disposed as soon as you don't need them - we cannot depend on the Garbage Collector.
For this, I have decided to enforce reference counting like Objective-C. You call retain()
when you want to own something, and release()
when not anymore. This increases/decreases a counter. When the counter hits 0
, dispose()
is called - at which point, Texture
will gladly free its resources such as pixel data etc.
NOTE
The reason I can't depend on the Garbage Collector, is because things like the pixel data aren't stored in Java, but rather by OpenGL in some bindings. Similar things happen with audio data. So it is necessary to call OpenGL to free that data manually.
On my first attempt, I made an interface:
public interface Retainable {
void retain();
void release();
void dispose();
}
So a class that wants to be part of this MRC scheme would be like
class Texture implements Retainable {
int retainCount = 1; // 1 because I want it to begin retained
public void retain() {
retainCount += 1;
}
public void release() {
if (--retainCount == 0) {
dispose();
}
}
public void dispose() {
// Free pixel data
}
}
This works and is OK.
However, every class that wants to do be part of the MRC scheme would need to:
- Create an
int
field - Implement
retain()
- Implement
release()
- Implement
dispose()
From the four points, the only non-redundant point is the last one. Obviously dispose()
will differ from one class to another. But all the other points won't differ from class to class - I am repeating code, and I don't like that.
On an attempt to improve the simplicity of this scheme, I decided to make a manager class to keep track of all Retainable
objects. The class will have a map, where the keys are the Retainable
objects and the values are their integer counts. The class will have retain(obj)
and release(obj)
, which increase/decrease the count in the map. When the count hits 0
it will call dispose()
and then remove the object from the map.
public abstract class Manager {
private static HashMap<Object, Integer> data = new HashMap<Object, Integer>();
public static void retain(Retainable object) {
if (data.containsKey(object)) {
data.put(object, data.get(object) + 1);
}else{
data.put(object, 1);
}
}
public static void release(Retainable object) {
if (data.containsKey(object)) {
int count = data.get(object);
if (count == 1) {
data.remove(object);
object.dispose();
}else{
data.put(object, count - 1);
}
}
}
}
While I understand that "global" stuff isn't likeable, I decided to sacrifice that for the sake of simplifying this scheme as much as possible.
I really like this approach, because now my classes don't need to implement so much redundant stuff. They only need to implement dispose()
.
There is one thing that still bugs me
I would like that, when you construct a Retainable
object, it will automatically have a retain count of 1
(so you don't need to call retain()
every time you make a new object).
Then, this means that a Retainable
class needs to call
Manager.retain(this);
In their constructor... for all classes. Ahh, if only there was a way to construct objects already retained without having to add this line in their constructor (I know this is a super small and almost meaningless thing, but heck...).
Do you have any ideas to improve this? And, very hopefully, a solution to the annoyance above?
ReferenceCounted
(and its implementations). It is finely-tuned, battle-tested, and scales well enough for Twitter. (The two lead authors work for Apple and Twitter.) \$\endgroup\$