【发布时间】:2017-11-27 19:50:19
【问题描述】:
我编写了一个 N-Body 模拟 JavaFX 程序,它将模拟的物体显示为球体。不幸的是,我正在努力解决内存泄漏问题。
我发现当从容器中删除球体时,即使不存在对球体的引用(至少从我的代码中),也不会释放为球体分配的内存。
很容易重现该行为:以下代码本质上是创建 JavaFX 项目时由 Eclipse 自动生成的 JavaFX 代码。我添加了一个按钮和一个组(用作球体的容器)。单击该按钮会调用 createSpheres 方法,该方法每次单击都会将 500 个球体添加到容器中,并在控制台中显示已使用的(堆)内存。
package application;
import java.util.Random;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Sphere;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
// --- User defined code -----------------
VBox root = new VBox();
Button btn = new Button("Add spheres...");
Group container = new Group();
root.getChildren().addAll(btn, container);
btn.setOnAction(e -> {
createSpheres(container);
});
// ---------------------------------------
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
// --- User defined code ------------------------------------------------------------------------
// Each call increases the used memory although the container is cleared and the GC is triggered.
// The problem does not occur when all spheres have the same radius.
// ----------------------------------------------------------------------------------------------
private void createSpheres(Group container) {
container.getChildren().clear();
Runtime.getRuntime().gc();
Random random = new Random();
for (int i = 0; i < 500; i++) {
//double d = 100; // OK
double d = 100 * random.nextDouble() + 1; // Problem
container.getChildren().add(new Sphere(d));
}
System.out.printf("Spheres added. Total number of spheres: %d. Used memory: %d Bytes of %d Bytes.\n",
container.getChildren().size(),
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(),
Runtime.getRuntime().maxMemory());
}
// ----------------------------------------------------------------------------------------------
}
当我启动程序时,每次单击都会增加使用的内存,尽管在方法开始时使用 container.getChildren().clear() 清除了容器(正如预期的那样,调用会删除先前单击按钮的球体但是使用的内存进一步增加)。 Runtime.getRuntime().gc() 的以下调用也无效(正如预期的那样,gc 已启动但内存未释放)。似乎仍然从某个地方引用了这些球体,或者如果没有处理资源,大概是在 JavaFX 代码中。
程序的输出如下所示: 我使用了最大 4GB 的 JVM 堆(-Xmx4g)。单击大约 30 次后,达到最大堆大小,应用程序因内存不足异常而崩溃。还显示了直接在添加球体之前和引发异常之后的 Visual VM 输出。后者显示了一个几乎完整的“老一代”池。最后一张图显示了添加球体期间的堆。尽管 GC 执行了 106 次回收,但没有释放内存。
Visual VM: Heap before the spheres are added
Visual VM: Heap after the spheres are added and the out-of-memory-exception was thrown
Visual VM: Heap during the adding of the spheres
值得注意的是,行为取决于球体的半径。在示例代码中,每个球体都有一个伪随机半径。当 SAME 半径用于所有球体时(与值无关),例如new Sphere(100),使用的内存保持不变,即内存以适当的方式释放!但是具有不同半径的球体越多,应用程序消耗的内存就越多。下图显示了球体具有相同半径时的内存。内存被释放。
Visual VM: Heap during the adding of the spheres in the case of identical radii
我使用 JDK-9.0.1/JRE-9.0.1 和 Eclipse Oxygen.1a Release (4.7.1a),Build id: 20171005-1200 以及 JRE1.8.0_151 和 Eclipse Neon.3 Release (4.6.3 ),内部版本号:20170314-1500。我的操作系统是 Windows 7 Ultimate,64 位。
有人知道我该如何解决这个问题(如果可能的话)还是它实际上是 JavaFX 内存泄漏问题?
【问题讨论】:
-
按照here的建议,您的分析器可以执行车库收集;我看到结果是一样的。
标签: javafx memory-leaks javafx-3d