2

为了能够保存我的 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,其中大部分来自两个ArrayLists。较小的包含 的实例,javax.media.j3d.RenderMolecule较大的包含 Object 的实例,每个实例都包含javax.media.j3d.RenderAtomListInfojavax.media.j3d.RenderMolecule

任何人都可以建议我,我做错了什么?

更新

我想澄清的是,即使doRender根本没有调用(例如,应用程序已启动并且没有采取更多操作),内存仍在累积。下面我将添加可以更好地显示情况的图像。

第一个是空闲运行应用程序的内存图。 记忆力

第二个是对象占用内存的饼图。这(a)是为 的实例分配的内存OffScreenCanvas3D(b)是所有其他对象占用的内存。 内存饼图

同样在下面,您可以看到dirtyDlistPerRinfoListdirtyRenderMoleculeList占用大部分空间。任何带有dirty前缀的东西都给我一种代码不好的感觉,我不知道为什么 在此处输入图像描述

更新2

看来问题出在接下来的部分:

  1. 的对象dirtyDlistPerRinfoList被添加到类的updateCanvasResource方法中RenderBin。这发生在所有画布上。
  2. dirtyDlistPerRinfoListupdateDirtyDisplayLists在类的方法中被清除RenderBin
  3. updateDirtyDisplayLists对于每个正在渲染的 Canvas3D,在类中从doWork(可怕的 1300 行方法)调用。Renderer

事情是offScreen画布不是一直在渲染,而是在图像将要保存的那一刻。是的,保存图像后,所有累积的内存dirtyDlistPerRinfoList都会被释放。

所以接下来的主要问题是:

的数据dirtyDlistPerRinfoList不断添加到未渲染的画布中,因此不会删除内存。这是我的Java3D错误的错吗?

4

4 回答 4

2

没有什么。Java 3D 应用程序至少需要 25+ MB 内存以及在 RenderMolecule 和 RenderAtomListInfo 类中存储模型所需的内存。请注意,BufferedImages 也会根据它们的大小消耗大量内存。不时调用 System.gc() 可能会有所帮助。BufferedImages 不是那么容易删除。

于 2012-12-04T11:05:47.380 回答
1

在被调用之后,您OffScreenCanvas3D恰好是该方法的主题。您可以通过覆盖来证明这一点。添加:doRenderRenderer.doWorkpreRender()

@Override
public void preRender() {
    System.out.println("OffScreenCanvas3D preRender !!!!!!!!!!!!!!!");
}

实例的脏列表将在之后被清除(如上所述)并在下一个快照时重新使用。

有时名称可能会产生误导。带有前缀“dirty”的列表包含强制下一个渲染循环进行必要更新的对象。

Java 3D 肯定会创建大量冗余数据来将场景图数据与渲染数据分开。您的好处之一是您可以随时在任何用户线程上调用(几乎)任何 Java 3D API 方法。无需将 Java 3D 代码封装在Runnables 中,因为 Swing 和 JavaFX 交互或其他构造需要它。

于 2012-12-07T13:29:25.620 回答
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

于 2012-12-10T10:38:23.290 回答
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;
}
于 2012-12-04T14:33:55.633 回答