patternjavaMinor
Java Manual-Reference-Counting
Viewed 0 times
referencemanualcountingjava
Problem
I have a Java game development framework. Certain classes, such as
For this, I have decided to enforce reference counting like Objective-C. You call
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:
So a class that wants to be part of this MRC scheme would be like
This works and is OK.
However, every class that wants to do be part of the MRC scheme would need to:
From the four points, the only non-redundant point is the last one. Obviously
On an attempt to improve the simplicity of this scheme, I decided to make a manager class to keep track of all
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
intfield
- 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 claSolution
You should not be manually counting references. Instead, you should be letting the garbage collector do the reference management for you, and then hook in to the garbage collection framework when the reference is cleared in Java.
The right tool for the job is the
the usual purpose of finalize, however, is to perform cleanup actions before the object is irrevocably discarded. For example, the finalize method for an object that represents an input/output connection might perform explicit I/O transactions to break the connection before the object is permanently discarded.
Note:
The Following 'Old' answer is based on the first version of the question that made no reference to non-Java memory....
I believe you have a basic misunderstanding of Java memory management. What you say you are doing, you are not actually doing, and this statement 'This works and is OK' is not what you think is happening.
You cannot possibly tell java to release the memory for an object. What you can do though, is remove all references to an object, and, at some point Java GC will scan through and identify that memory as no longer used, and available for reuse.
In your class, you have the 'pixel data', perhaps an array of
This is what you are seeing, and you think 'it works'. But, that is not the solution you should be using.
What you should be doing, is not referencing the texture when it is no longer needed!
In other words, there's a bigger picture here... there is somewhere in you code where you have done:
and then, you have a reference to
If you say
Instead of finding all the places in your code that use the texture, and calling
If you are not releasing your pixels after doing that, it means you have some place in your code where you are still holding references to the Texture, perhaps some value in a HashMap, an array, or somewhere else where you are holding a reference. Remove those entries, and you will be fine.
Note: it is possible that your program is part of a very, very, tiny, small proportion of programs that may need finer memory control, and need to keep a Texture cached even though no-one is using it at a particular moment in time. Java has Soft-References that may help, but that is not likely to be your solution, and is more complicated than this post warrants....
The right tool for the job is the
finalize() method. This often-misused method, is actually purpose-designed for this exact case:the usual purpose of finalize, however, is to perform cleanup actions before the object is irrevocably discarded. For example, the finalize method for an object that represents an input/output connection might perform explicit I/O transactions to break the connection before the object is permanently discarded.
public class Texture {
....
@Override
protected void finalize() {
// clear resources kept in external areas to Java....
.....
}
}Note:
The Following 'Old' answer is based on the first version of the question that made no reference to non-Java memory....
I believe you have a basic misunderstanding of Java memory management. What you say you are doing, you are not actually doing, and this statement 'This works and is OK' is not what you think is happening.
You cannot possibly tell java to release the memory for an object. What you can do though, is remove all references to an object, and, at some point Java GC will scan through and identify that memory as no longer used, and available for reuse.
In your class, you have the 'pixel data', perhaps an array of
private byte[] pixels = ..., and in your dispose() method you call pixels = null, which removes the reference to the byte[] array. When the Garbage collector sweeps the heap, it will identify that the byte[] array is no longer referenced, and it will release that space.This is what you are seeing, and you think 'it works'. But, that is not the solution you should be using.
What you should be doing, is not referencing the texture when it is no longer needed!
In other words, there's a bigger picture here... there is somewhere in you code where you have done:
Texture mytexture = new Texture(pixeldata);and then, you have a reference to
mytexture that you are not releasing.If you say
mytexture = null, then the garbage collector will release the pixel data as well as the texture object.Instead of finding all the places in your code that use the texture, and calling
retain() and release() they should just set their Texture variable to null, and move on if they no longer need the Texture.If you are not releasing your pixels after doing that, it means you have some place in your code where you are still holding references to the Texture, perhaps some value in a HashMap, an array, or somewhere else where you are holding a reference. Remove those entries, and you will be fine.
Note: it is possible that your program is part of a very, very, tiny, small proportion of programs that may need finer memory control, and need to keep a Texture cached even though no-one is using it at a particular moment in time. Java has Soft-References that may help, but that is not likely to be your solution, and is more complicated than this post warrants....
Code Snippets
public class Texture {
....
@Override
protected void finalize() {
// clear resources kept in external areas to Java....
.....
}
}Texture mytexture = new Texture(pixeldata);Context
StackExchange Code Review Q#52553, answer score: 5
Revisions (0)
No revisions yet.