2023-02-16 375
我写了一个N体模拟Javafx程序,该程序将模拟的身体显示为球体.不幸的是,我正在为内存泄漏而苦苦挣扎.
我发现,即使没有参考(至少从我的代码中)对球体删除,分配给球体的内存也不会释放.
很容易复制行为:以下代码本质上是当创建Javafx项目时通过Eclipse自动生成的Javafx代码.我添加了一个按钮和一个组(作为球体的容器).单击按钮调用方法创建方法,每次点击500个球体都会在容器中添加500个球,并在控制台中显示已使用的(HEAP)内存.
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,但内存未释放).似乎仍然是从某个地方引用的球体,或者如果没有处置资源,大概是在Javafx代码中.
程序的输出如下:我使用了4GB(-XMX4G)的最大JVM堆.大约30次点击后,达到了最大堆大小,并且应用程序崩溃,并且具有空格异常.还显示的是直接在添加球体和异常之后的视觉VM输出.后者显示了一个几乎完整的”旧一代”池.最后一个图显示了在添加球体期间的堆.尽管GC执行了106个集合,但没有释放记忆.
程序控制台输出
Visual VM:添加球体之前的堆
visual vm:添加了球后堆,并添加了内存之外 – 投掷
visual vm:在添加领域期间堆
值得注意的是,行为取决于球体的半径.在示例代码中,每个球体都有一个伪随机半径.当所有球体使用相同的半径(独立于值)时,例如新球(100),使用的内存保持恒定,即以正确的方式释放内存!但是,该应用程序消耗越多的半径越多的球.下图显示了当球体具有相同的半径时的内存.记忆被释放.
Visual VM:在相同的radii
我使用JDK-9.0.1/jre-9.0.1和Eclipse oxygen.1A版本(4.7.1a),构建ID:20171005-1200以及JRE1.8.0_151和Eclipse Neon.3版本(4.6.3.3.3.3.3 ),构建ID:20170314-1500.我的操作系统是Windows 7 Ultimate,64位.
有人知道我如何解决问题(如果可能的话)或实际上是Javafx内存泄漏问题?
您的问题的答案可以在名为javafx.scene.shape.PredefinedMeshManager的包装保护类中找到.
每次创建Sphere/Box/Cylinder 3D形状时,TriangleMesh对象都会基于给定的键添加到HashMap中.
对于球体,此键基于其半径和划分数:
private static int generateKey(float r, int div) {
int hash = 5;
hash = 23 * hash + Float.floatToIntBits(r);
hash = 23 * hash + div;
return hash;
}
在测试中,您没有修改部门的数量,因此,当您使用相同的半径对所有500球使用相同的半径时,您正在为所有这些键生成相同的密钥,因此Manager Hashmap将始终包含一个单个元素.
这对于有几个球体具有完全相同的网格的常规情况非常方便:您不必再生成这些网格,只需一次即可缓存网格.
相反,如果您对球体的半径不同,则键将始终不同,并且在每次点击时,您都会在hashmap中添加500个新对象.
当您用球体清洁容器时,经理不知道这一点,也不会将其从哈希图中删除,因此计数会增加,直到您获得内存异常.
.
通过反射,我在添加500个球的同时设法监视了sphereCache的大小,直到达到内存例外:
Spheres added. Total number of spheres: 500. Used memory: 7794744 Bytes of 3817865216 Bytes.
sphereCache: javafx.scene.shape.PredefinedMeshManager@cb26fc7 Size: 500
Spheres added. Total number of spheres: 500. Used memory: 147193720 Bytes of 3817865216 Bytes.
sphereCache: javafx.scene.shape.PredefinedMeshManager@cb26fc7 Size: 1000
...
Spheres added. Total number of spheres: 500. Used memory: 3022528400 Bytes of 3817865216 Bytes.
sphereCache: javafx.scene.shape.PredefinedMeshManager@cb26fc7 Size: 11497
Spheres added. Total number of spheres: 500. Used memory: 3158410200 Bytes of 3817865216 Bytes.
sphereCache: javafx.scene.shape.PredefinedMeshManager@cb26fc7 Size: 11996
Spheres added. Total number of spheres: 500. Used memory: 3292418936 Bytes of 3817865216 Bytes.
sphereCache: javafx.scene.shape.PredefinedMeshManager@cb26fc7 Size: 12185
Exception in thread "JavaFX Application Thread" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3284)
at com.sun.javafx.collections.ObservableIntegerArrayImpl.ensureCapacity(ObservableIntegerArrayImpl.java:254)
at com.sun.javafx.collections.ObservableIntegerArrayImpl.setAllInternal(ObservableIntegerArrayImpl.java:131)
at com.sun.javafx.collections.ObservableIntegerArrayImpl.setAll(ObservableIntegerArrayImpl.java:156)
at javafx.scene.shape.Sphere.createMesh(Sphere.java:420)
at javafx.scene.shape.PredefinedMeshManager.getSphereMesh(PredefinedMeshManager.java:68)
at javafx.scene.shape.Sphere.impl_updatePeer(Sphere.java:157)
at javafx.scene.Node.impl_syncPeer(Node.java:503)
at javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2290)
显然,如果我们可以访问该缓存,我们可以防止此内存泄漏:
private void createSpheres(Group container) {
container.getChildren().clear();
if (sphereCache != null) {
sphereCache.clear();
}
...
}
可能您可能需要提交问题在这里.
以上所述是小编给大家介绍的在JavaFX 3D中删除对象时内存泄漏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!
原文链接:https://77isp.com/post/33957.html
=========================================
https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。
数据库技术 2022-03-28
网站技术 2022-11-26
网站技术 2023-01-07
网站技术 2022-11-17
Windows相关 2022-02-23
网站技术 2023-01-14
Windows相关 2022-02-16
Windows相关 2022-02-16
Linux相关 2022-02-27
数据库技术 2022-02-20
抠敌 2023年10月23日
嚼餐 2023年10月23日
男忌 2023年10月22日
瓮仆 2023年10月22日
簿偌 2023年10月22日
扫码二维码
获取最新动态