为了保存我的3D画布的快照,我在下一步扩展了Canvas3D
:
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() {}
}
我将它添加为宇宙视图。这里描述了一个主要策略:http://www.java2s.com/Code/Java/3D/PrintCanvas3D.htm
问题在于内存泄漏。我的应用程序开始崩溃,当我尝试配置文件时,我发现OffScreenCanvas3D
的实例几乎占据 50MB ,其中大部分来自两个ArrayList
秒。较小的一个包含javax.media.j3d.RenderMolecule
的实例,较大的一个包含Object的实例,每个实例包含javax.media.j3d.RenderAtomListInfo
和javax.media.j3d.RenderMolecule
。
有人可以建议我,我做错了吗?
我想澄清一点,即使根本没有调用doRender
(例如,应用程序已启动且不再执行任何操作),内存仍在累积。下面我将添加可以更好地显示情况的图像。
第一个是空闲运行应用程序的内存图。
第二个是对象占用的内存饼图。这里(a)
是为OffScreenCanvas3D
实例分配的内存,(b)
是所有其他对象所占用的内存。
同样在下方,您可以看到dirtyDlistPerRinfoList
和dirtyRenderMoleculeList
占用了大部分空间。任何带有 dirty 前缀的东西都让我感觉代码不好,我不知道为什么
问题似乎在下一部分:
dirtyDlistPerRinfoList
类的updateCanvasResource
方法中添加了RenderBin
的对象。所有画布都会发生这种情况。dirtyDlistPerRinfoList
已在updateDirtyDisplayLists
类的RenderBin
方法中清除。updateDirtyDisplayLists
在doWork
(一个可怕的1300行方法)中被调用Renderer
。有一点是offScreen画布不是一直在渲染,而是在图像保存的那一刻。是的,保存图像后,dirtyDlistPerRinfoList
中累积的所有内存都被释放。
接下来的主要问题是:
dirtyDlistPerRinfoList
的数据会不断添加到未呈现的画布中,因此不会删除内存。这是我的Java3D错误吗?
答案 0 :(得分:2)
无。 Java 3D应用程序需要至少25 MB以上的内存以及在RenderMolecule和RenderAtomListInfo类中存储模型所需的内存。请注意,BufferedImages也会消耗大量内存,具体取决于它们的大小。不时调用System.gc()可能会有所帮助。 BufferedImages并不容易删除。
答案 1 :(得分:1)
在OffScreenCanvas3D
被调用后,doRender
只是Renderer.doWork
方法的主题。您可以通过覆盖preRender()
来证明这一点。添加:
@Override
public void preRender() {
System.out.println("OffScreenCanvas3D preRender !!!!!!!!!!!!!!!");
}
之后将清除实例的脏列表(如上所述),并在下一次快照时重新使用。
有时名字可能会产生误导。带有前缀“dirty”的列表包含强制下一个渲染循环必要更新的对象。
Java 3D肯定会创建大量冗余数据,以便从渲染数据中分割场景图数据。您的好处是,您可以随时在任何用户线程上调用(几乎)任何Java 3D API方法。无需在Runnable
中封装Java 3D代码,因为它是Swing和JavaFX交互或其他构造所必需的。
答案 2 :(得分:1)
JDK工具JConsole允许监视应用程序的内存使用情况。您可以执行垃圾收集器(GC)并检查是否释放了内存。
在程序中调用'java.lang.System.gc()'会产生同样的效果。
Java 3D应用程序本身没有内存泄漏。在撰写这篇文章时,4-Canvas3D示例程序PropellerUniverse正在运行并由JConsole监控。每隔几秒钟,JRE就会自动开始垃圾回收。堆内存使用量为25 MB,非堆内存使用量为34 MB。该程序现在运行25分钟。 http://www.interactivemesh.org/testspace/j3dmeetsswing.html#heavyweight
答案 3 :(得分:0)
尝试消除每一步,直到您找到仍然存在问题的示例。仅执行以下操作时问题是否仍然存在?
// 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;
}