Reputation: 13396
To enable saving of a snapshot of my 3d canvas, I've extended Canvas3D
in the next way:
class OffScreenCanvas3D extends Canvas3D {
OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
super(graphicsConfiguration, offScreen);
}
public BufferedImage doRender(int width, int height) {
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, bImage);
setOffScreenBuffer(buffer);
renderOffScreenBuffer();
waitForOffScreenRendering();
bImage = getOffScreenBuffer().getImage();
setOffScreenBuffer(null);
return bImage;
}
public void postSwap() {}
}
And I add it as a view to the universe. And a main strategy is described here: http://www.java2s.com/Code/Java/3D/PrintCanvas3D.htm
The problem is in memory leak. My app started to crash and when I tried to profile, I've found out that an instance of OffScreenCanvas3D
occupies almost 50MB and the most part of it comes from two ArrayList
s. Smaller one contains instances of javax.media.j3d.RenderMolecule
and a larger one contains instances of Object each containing javax.media.j3d.RenderAtomListInfo
and javax.media.j3d.RenderMolecule
.
Can anybody suggest me, what I'm doing wrong?
I want to clarify that even when doRender
is not called at all (eg. applications is launched and no more actions are taken) the memory is still accumulating. Below I'll add images that will show the situation better.
First one is a memory graph of an idle running application.
The second one is a pie chart of memory taken by objects. Here (a)
is the memory allocated for an instance of OffScreenCanvas3D
, (b)
is the memory ocupied by all the other objects.
Also below you can see that dirtyDlistPerRinfoList
and dirtyRenderMoleculeList
take most of the space. Anything with prefix dirty gives me a feeling of a bad code, I don't know why
It seems that the problem is in the next parts:
dirtyDlistPerRinfoList
are added in the updateCanvasResource
method of the RenderBin
class. This happens with all the canvas.dirtyDlistPerRinfoList
is cleared in updateDirtyDisplayLists
method of the RenderBin
class.updateDirtyDisplayLists
is called from doWork
(a horrible 1300 lines method) in class Renderer
for every Canvas3D that's being rendered.A thing is that offScreen canvas is not being rendered all the time, but just in the moment when the image is going to be saved. And yes, after saving the image, all the memory accumulated in dirtyDlistPerRinfoList
is freed.
So the main question is next:
Data of
dirtyDlistPerRinfoList
is continuously added to canvas that's not being rendered and so the memory won't be deleted. Is this my fault of Java3D bug?
Upvotes: 2
Views: 937
Reputation: 11
The JDK tool JConsole allows watching the memory usage of your application. You can perform the garbage collector (GC) and check if memory is freed.
Calling 'java.lang.System.gc()' in your program would have the same effect.
Java 3D applications don't per se have memory leaks. While writing this post the 4-Canvas3D sample program PropellerUniverse is running and is monitored by JConsole. Every few seconds the JRE starts garbage collection automatically. The heap memory usage is at 25 MB and the non-heap memory usage is at 34 MB. The program is now running for 25 minutes. http://www.interactivemesh.org/testspace/j3dmeetsswing.html#heavyweight
Upvotes: 1
Reputation: 11
Your OffScreenCanvas3D
is exactly once, after doRender
is called, subject of the Renderer.doWork
method. You can prove this by overriding preRender()
. Add:
@Override
public void preRender() {
System.out.println("OffScreenCanvas3D preRender !!!!!!!!!!!!!!!");
}
The instance's dirty lists will be cleared afterwards (as you mentioned above) and re-used at the time of next snapshot.
Sometimes names might be misleading. Lists with the prefix "dirty" contain objects that force necessary updates for the next rendering loop.
Java 3D definitely creates a lot of redundant data to split your scene graph data from rendering data. Your benefit amongst others is that you are allowed to call (almost) any Java 3D API method on any user thread at any time. No need to encapsulate Java 3D code in Runnable
s as it is required for Swing and JavaFX interaction or into other constructs.
Upvotes: 1
Reputation: 1539
Try to eliminate every step until you arrive at an example that still exhibits the problem. Does the problem still occur when only doing the following?
// Moved this to be a field
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, bImage);
OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
super(graphicsConfiguration, offScreen);
// set this just once
setOffScreenBuffer(buffer);
}
public BufferedImage doRender(int width, int height) {
renderOffScreenBuffer();
waitForOffScreenRendering();
// not necessary, image does not change
// bImage = getOffScreenBuffer().getImage();
// why is this necessary?
// setOffScreenBuffer(null);
return bImage;
}
Upvotes: 0
Reputation: 21
Nothing. A Java 3D application requires a minimum of 25+ MB memory plus the memory needed to store your model(s) in RenderMolecule and RenderAtomListInfo classes. Please be aware that BufferedImages also consume a lot of memory depending on their size. Calling System.gc() from time to time might help. BufferedImages are not that easy to remove.
Upvotes: 2