4

I am using a 64 bit linux machine (8GB of RAM) on KDE with Eclipse as my IDE. I am also using Oracle's JDK. I made a small animation using JavaFX and a few pictures off the web to animate earth rotating around the sun. Whenever I run it, the animation works normally, but it steadily eats all of the RAM on my computer until everything freezes. This usually takes less than 5 minutes.

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();
    }
}

I've set -Xms512m, -Xmx512m, and -Xss512m. Is there something I'm doing wrong that could be causing this, and could you explain why that happens or how to avoid it?

Also if there is something wrong with my question, please let me know.

Edits: Added more information

The Earth image is 2356x2356, and I set it to 25x25px in the program. The Sun image is 750x750, and I set it to 50x50 in the program . The space image is 1920x1080, and it is the background which is 512x512 px.

Links to images

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

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

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

Jal
  • 65
  • 1
  • 8

3 Answers3

1

gc.drawImage(space, 0, 0); is causing the problem. IMHO, you are not supposed to call it for every frame.

Normally we do animation by rendering frames. We get a Graphics object and for each frame we clear the canvas and redraw everything. But that's not how things work in JavaFX.

In JavaFX, animation is achieved by applying transformation on nodes - shapes, images or groups. You setup the scene, add your "actors", the shapes, images etc. Then you create Animation objects that control those "actors".

I am no expert, so the following example just demonstrate the idea of how to make a circle rotate around another. The motion is not uniform. So you definitely want to experiment different paths/Transitions/Animations.

Update: use Path.setInterpolator(Interpolator.LINEAR) to remove the acceleration

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();
    }
}
xiaofeng.li
  • 8,237
  • 2
  • 23
  • 30
1

I can't see anything wrong in your code. It may not be the best way to do this in JavaFX but it looks perfectly valid to me and should not eat up any memory. Especially as you say that you have the same problem with the other code from Luke I suspect some Linux bug. Have you tried running your program on another OS? If you'd provide the links to your images someone else could try that too.

This link may be related to your problem: javafx-unexplainable-leaks-memory-on-linux

Test

I ran your program on a Mac and there was no memory leak and almost no CPU usage, as I'd expected.

Community
  • 1
  • 1
mipa
  • 10,369
  • 2
  • 16
  • 35
  • I once helped solve a bug which was also insane memory on linux. The problem lied within the hardware acceleration being buggy, you can use the `-Dprism.order=sw` JVM argument to use software rendering. – randers Dec 02 '16 at 15:41
0

tl;dr Use the -D.prism=sw argument for the JVM to hack around the problem.

JavaFX tries to make use of hardware acceleration, and it seems that the new Mesa and Xorg drivers don't entirely solve the issue. I also think that the VDPAU driver, the hardware acceleration driver, is at fault. When I run the program with a -D.prism=sw argument for the JVM, which sets the compiler to use software pipelining rather than hardware acceleration, the problem is greatly reduced. I still see that the program steadily consumes memory, but the process is much slower.

I also found that reducing the number of times the gc.drawimage() is called also increases the amount of time it takes to fill my 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();

There may be an issue with garbage collection there, but I'm not sure. I will use VisualVM to check later, then I'll update this answer.

Update There is no issue with garbage collection. The memory is stable now, and it was stabilized by enabling software pipelining. JavaFX seems to not work well with VDPAU. Thanks for all the help!

Sources:

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

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

Jal
  • 65
  • 1
  • 8