【问题标题】:JVM consumes all of my RAM for 40 lines of codeJVM 为 40 行代码消耗了我所有的 RAM
【发布时间】:2016-12-01 00:35:51
【问题描述】:

我在 KDE 上使用 64 位 linux 机器(8GB 内存),并将 Eclipse 作为我的 IDE。我也在使用 Oracle 的 JDK。我使用 JavaFX 和一些网上的图片制作了一个小动画,让地球围绕太阳旋转。每当我运行它时,动画都会正常运行,但它会稳定地吃掉我电脑上的所有 RAM,直到一切都冻结。这通常需要不到 5 分钟。

package Practice;
/**
 * For some reason, this code gobbles up memory, and freezes computers
 */

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class BasicAnimation extends Application {

    public BasicAnimation() {
        // TODO Auto-generated constructor stub
    }

    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Orbit");

        Group root = new Group();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);

        Canvas canvas = new Canvas(512,512);
        root.getChildren().add(canvas);

        GraphicsContext gc = canvas.getGraphicsContext2D();

            Image earth = new Image(getClass().getResourceAsStream("earth.png"), 25.0, 25.0 ,false, false);
        Image sun = new Image(getClass().getResourceAsStream("sun.jpg"), 50.0, 50.0, false, false);
        Image space = new Image(getClass().getResourceAsStream("space.jpg"));

        final long startNanoTime = System.nanoTime();

        new AnimationTimer() {

            public void handle(long currentNanoTime) {

                double t = (currentNanoTime - startNanoTime) / 1000000000.0 ;

                double x = 232 + 128 * Math.cos(t);
                double y = 232 + 128 * Math.sin(t);

                //background image clears canvas

                gc.drawImage(space, 0, 0);
                gc.drawImage(earth, x, y);
                gc.drawImage(sun, 196, 196);

            }
        }.start();

        primaryStage.show();
    }
}

我设置了 -Xms512m、-Xmx512m 和 -Xss512m。我做错了什么可能导致这种情况,你能解释为什么会发生这种情况或如何避免它吗?

另外,如果我的问题有问题,请告诉我。

编辑:添加更多信息

地球图片为2356x2356,我在程序中设置为25x25px。 Sun图像是750x750,我在程序中设置为50x50。空间图片为1920x1080,背景为512x512 px。

图片链接

孙:https://www.thesun.co.uk/wp-content/uploads/2016/06/download.jpg?w=750&strip=all

地球:https://openclipart.org/image/2400px/svg_to_png/218125/3d-Earth-Globe.png

空格:http://www.gunnars.com/wp-content/uploads/2014/08/Space.jpg

【问题讨论】:

标签: java javafx


【解决方案1】:

我看不出你的代码有什么问题。这可能不是在 JavaFX 中执行此操作的最佳方法,但它对我来说看起来完全有效,并且不应该占用任何内存。特别是当你说你对卢克的其他代码有同样的问题时,我怀疑是一些 Linux 错误。您是否尝试过在另一个操作系统上运行您的程序?如果您提供图片的链接,其他人也可以尝试。

此链接可能与您的问题有关: javafx-unexplainable-leaks-memory-on-linux

测试

我在 Mac 上运行了您的程序,没有内存泄漏,几乎没有 CPU 使用率,正如我所料。

【讨论】:

  • 我曾经帮助解决了一个错误,该错误也是 linux 上的疯狂内存。问题出在硬件加速有bug,可以使用-Dprism.order=swJVM参数来使用软件渲染。
【解决方案2】:

gc.drawImage(space, 0, 0); 导致问题。恕我直言,您不应该为每一帧都调用它。

通常我们通过渲染帧来制作动画。我们得到一个Graphics 对象,并且对于每一帧我们清除画布并重绘所有内容。 但这不是 JavaFX 中的工作方式。

在 JavaFX 中,动画是通过对节点(形状、图像或组)应用变换来实现的。您设置场景,添加“演员”、形状、图像等。然后创建控制这些“演员”的Animation 对象。

我不是专家,所以下面的例子只是演示了如何让一个圆围绕另一个圆旋转。运动不均匀。所以你肯定想尝试不同的路径/过渡/动画。

更新:使用Path.setInterpolator(Interpolator.LINEAR)取消加速

import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.BoxBlur;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;

import static javafx.animation.Animation.INDEFINITE;

public class Animation extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("orbit");

        Group root = new Group();
        Scene scene = new Scene(root, 800, 800, Color.BLACK);
        primaryStage.setScene(scene);

        Circle sun = new Circle(50, Color.web("yellow", 1.0));
        sun.setCenterX(400);
        sun.setCenterY(400);
        sun.setEffect(new BoxBlur(10, 10, 3));

        Circle earth = new Circle(10, Color.web("blue"));
        earth.setEffect(new BoxBlur(4, 4, 3));

        root.getChildren().add(sun);
        root.getChildren().add(earth);

        Path path = new Path();
        ArcTo arcTo = new ArcTo();
        arcTo.setX(20);
        arcTo.setY(401);
        arcTo.setSweepFlag(true);
        arcTo.setLargeArcFlag(true);
        arcTo.setRadiusX(400);
        arcTo.setRadiusY(400);
        arcTo.setXAxisRotation(0);

        path.getElements().add(new MoveTo(20, 400));
        path.getElements().add(arcTo);
        path.getElements().add(new ClosePath());
        path.setVisible(false);

        PathTransition pt = new PathTransition(Duration.seconds(10), path, earth);
        pt.setInterpolator(Interpolator.LINEAR); // No acceleration/deceleration
        pt.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
        pt.setCycleCount(INDEFINITE);
        pt.play();

        primaryStage.show();
    }
}

【讨论】:

  • 我试过这个,我遇到了同样的问题。不过还是谢谢你的建议!
【解决方案3】:

tl;dr 使用 JVM 的 -D.prism=sw 参数来解决问题。

JavaFX 试图利用硬件加速,新的 Mesa 和 Xorg 驱动似乎并不能完全解决这个问题。我还认为硬件加速驱动程序 VDPAU 驱动程序有问题。当我使用 JVM 的 -D.prism=sw 参数运行程序时,它将编译器设置为使用软件流水线而不是硬件加速,问题大大减少了。我仍然看到程序稳定地消耗内存,但过程要慢得多。

我还发现减少调用 gc.drawimage() 的次数也会增加填充 RAM 所需的时间。

new AnimationTimer() { //This is how I reduced the number of times gc.drawImage is called

        long lastupdate = 0 ;

        public void handle(long currentNanoTime) {

            double t = (currentNanoTime - startNanoTime) / 1000000000.0 ;

            double x = 232 + 128 * Math.cos(t);
            double y = 232 + 128 * Math.sin(t);

            //background image clears canvas

            if(currentNanoTime - lastupdate >= 33333333) {
                gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
                gc.drawImage(space, 0, 0);
                gc.drawImage(sun, 196, 196);
                gc.drawImage(earth, x, y);

                lastupdate = currentNanoTime;
            }


        }
    }.start();

那里的垃圾收集可能有问题,但我不确定。稍后我将使用 VisualVM 进行检查,然后我将更新此答案。

更新 垃圾收集没有问题。内存现在很稳定,通过启用软件流水线来稳定。 JavaFX 似乎不适用于 VDPAU。感谢大家的帮助!

来源:

https://github.com/jfoenixadmin/JFoenix/issues/52

https://bugs.openjdk.java.net/browse/JDK-8161997

【讨论】:

    猜你喜欢
    • 2018-03-21
    • 2018-12-18
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多