Хобрук: Ваш путь к мастерству в программировании

Утечка памяти в неопределенной временной шкале JavaFX

Я разрабатываю секундомер с использованием JavaFX. Код работает хорошо. За исключением огромных кумулятивных утечек памяти с течением времени. Утечка увеличивается всякий раз, когда я увеличиваю Timeline framerate. В настоящее время я использую Ubuntu 16.04 с 4 гигабайтами оперативной памяти, и утечка происходит на скорости 300 МБ / мин при 30 кадрах в секунду. Это 5 МБ/с. Я понимаю, что это может произойти из-за повторяющегося рисования над Scene, но почему это должно быть кумулятивным? Разве JVM не должна позаботиться об этом?

Main.java:

package UI;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.setTitle("StopWatch");
        primaryStage.setScene(new Scene(getPane(), 400, 400));
        primaryStage.show();
    }


    private BorderPane getPane(){
        BorderPane pane = new BorderPane();

        ClockUI clockUI = new ClockUI();
        clockUI.setMinSize(200,200);
        pane.setCenter(clockUI);

        ButtonBar buttonBar = new ButtonBar();
        Button startButton = new Button("Start");
        startButton.setOnAction(e->clockUI.startClock());
        Button pauseButton = new Button("Stop");
        pauseButton.setOnAction(e->clockUI.stopClock());
        Button resetButton = new Button("Reset");
        resetButton.setOnAction(e->clockUI.resetClock());
        buttonBar.getButtons().addAll(startButton, pauseButton, resetButton);
        pane.setBottom(buttonBar);

        return pane;
    }

    public static void main(String[] args) {
        System.setProperty("prism.lcdtext","false");
        launch(args);
    }
}

ClockUI.java:

package UI;

import javafx.animation.*;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;

/**
 * Created by subhranil on 23/6/17.
 */
public class ClockUI extends StackPane {

    private final Rotate hourRotate;
    private final Rotate minuteRotate;
    private final Rotate secondRotate;
    private final Timeline hourTimeline;
    private final Timeline minuteTimeline;
    private final Timeline secondTimeline;
    private final ParallelTransition clockTransition;

    public ClockUI() {
        super();

        Line hourHand = getHand(80, Color.WHITE);
        hourRotate = getRotate(hourHand);
        hourTimeline = createRotateTimeline(Duration.hours(12), hourRotate);

        Line minuteHand = getHand(100, Color.WHITE);
        minuteRotate = getRotate(minuteHand);
        minuteTimeline = createRotateTimeline(Duration.minutes(60), minuteRotate);

        Line secondHand = getHand(90, Color.WHITE);
        secondRotate = getRotate(secondHand);
        secondTimeline = createRotateTimeline(Duration.seconds(60), secondRotate);

        clockTransition = new ParallelTransition(hourTimeline, minuteTimeline, secondTimeline);

        Circle back = new Circle(120);
        back.centerXProperty().bind(widthProperty().divide(2));
        back.centerYProperty().bind(heightProperty().divide(2));
        back.setStyle("-fx-fill: #555555");
        setStyle("-fx-background-color: #333333;");

        getChildren().addAll(back, hourHand, minuteHand, secondHand);
    }

    private Timeline createRotateTimeline(Duration duration, Rotate rotate) {
        Timeline timeline = new Timeline(30);
        timeline.getKeyFrames().add(new KeyFrame(duration, new KeyValue(rotate.angleProperty(), 360)));
        timeline.setCycleCount(Animation.INDEFINITE);
        return timeline;
    }

    public void startClock() {
        if (clockTransition.getStatus() != Animation.Status.RUNNING) {
            clockTransition.play();
        }
    }

    public void stopClock() {
        if (clockTransition.getStatus() == Animation.Status.RUNNING) {
            clockTransition.pause();
        }
    }

    public void resetClock() {
        stopClock();
        clockTransition.stop();
    }

    private Rotate getRotate(Line line){
        Rotate r = new Rotate(0);
        r.pivotXProperty().bind(line.startXProperty());
        r.pivotYProperty().bind(line.startYProperty());
        line.getTransforms().add(r);
        return r;
    }

    private Line getHand(int size, Paint color) {
        Line hand = new Line();
        hand.startXProperty().bind(widthProperty().divide(2));
        hand.startYProperty().bind(heightProperty().divide(2));
        hand.endXProperty().bind(widthProperty().divide(2));
        hand.endYProperty().bind(heightProperty().divide(2).subtract(size));
        hand.setStroke(color);
        hand.setStrokeWidth(3);

        return hand;
    }

}

ИНФОРМАЦИЯ. Я пробовал различные другие методы, такие как запуск ExecutorService, использование Task и Thread, но все они дают одинаковые результаты.


  • Какой JDK вы используете и какую версию? В последние месяцы были исправлены некоторые ошибки, связанные с управлением памятью в приложениях JavaFX. Сейчас я использую Oracle JDK 1.8.0_131-b11, и у меня больше нет этих проблем. 24.06.2017
  • ❯ java-версия openjdk version "1.8.0_131" OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11) OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode) ~ ❯ javac-версия javac 1.8.0_131 24.06.2017
  • Можете ли вы попробовать Oracle JDK? Я переключился несколько месяцев назад, потому что JavaFx не работал гладко с OpenJDK. 24.06.2017
  • Также см. stackoverflow.com/questions/40228866 / и stackoverflow.com/ вопросы/40899372/. 24.06.2017
  • @Axel в oracle jdk8, mesa 17.1.2, xorg 1.18.4, это все еще происходит. Даже при рендеринге sw с использованием -Dprism.order=sw происходят утечки памяти. 24.06.2017

Ответы:


1

Попробуйте это и посмотрите, есть ли у вас такая же проблема.

ЧасыGUI

import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;

/**
 *
 * @author Sedrick
 */
public class ClockGUI {

    Circle clockFace;
    Line second;
    Line minute;
    Line hour;
    Rotate secondRotation;
    Rotate minuteRotation;
    Rotate hourRotation;
    AnchorPane currentClockFace;

    public ClockGUI()
    {
        currentClockFace = new AnchorPane();
        currentClockFace.setPrefSize(100, 100);

        clockFace = new Circle(100 / 2, 100 / 2, 100 / 2);
        clockFace.setStroke(Color.BLACK);
        clockFace.setFill(Color.TRANSPARENT);

        second = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 40);
        secondRotation = new Rotate();
        secondRotation.pivotXProperty().bind(second.startXProperty());
        secondRotation.pivotYProperty().bind(second.startYProperty());
        second.getTransforms().add(secondRotation);

        minute = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 30);
        minuteRotation = new Rotate();
        minuteRotation.pivotXProperty().bind(minute.startXProperty());
        minuteRotation.pivotYProperty().bind(minute.startYProperty());
        minute.getTransforms().add(minuteRotation);

        hour = new Line(100 / 2, 100 / 2, 100 / 2, 100 / 2 - 20);
        hourRotation = new Rotate();
        hourRotation.pivotXProperty().bind(hour.startXProperty());
        hourRotation.pivotYProperty().bind(hour.startYProperty());
        hour.getTransforms().add(hourRotation);

        currentClockFace.getChildren().addAll(clockFace, second, minute, hour);

    }

    public AnchorPane getCurrentClock()
    {
        return currentClockFace;
    }

    public void rotateSecondLine()
    {
        secondRotation.setAngle(secondRotation.getAngle() + 6);
    }

    public double getRotateSecondLine()
    {
        return secondRotation.getAngle();
    }

    public void setRotateSecond(double degree)
    {
        secondRotation.setAngle(degree);
    }

    public void rotateMinuteLine()
    {
        minuteRotation.setAngle(minuteRotation.getAngle() + 6);
    }

    public double getRotateMinuteLine()
    {
        return minuteRotation.getAngle();
    }

    public void setRotateMinute(double degree)
    {
        minuteRotation.setAngle(degree);
    }

    public void rotateHourLine()
    {
        hourRotation.setAngle(hourRotation.getAngle() + 6);
    }

    public double getRotateHourLine()
    {
        return hourRotation.getAngle();
    }

    public void setRotateHour(double degree)
    {
        hourRotation.setAngle(degree);
    }
}

Главный

import javafx.animation.*;
import javafx.application.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.util.*;

/**
 *
 * @author Sedrick
 */
public class JavaFXApplication54 extends Application {

    @Override
    public void start(Stage primaryStage)
    {
        VBox root = new VBox();
        ClockGUI cgui = new ClockGUI();

        StackPane stackpane = new StackPane();
        stackpane.getChildren().add(cgui.getCurrentClock());
        root.getChildren().add(stackpane);

        Button btn = new Button("Rotate seconds");
        btn.setOnAction((event) -> {
            cgui.rotateSecondLine();
        });

        Button btn2 = new Button("Rotate minutes");
        btn2.setOnAction((event) -> {
            cgui.rotateMinuteLine();
        });

        Button btn3 = new Button("Rotate hours");
        btn3.setOnAction((event) -> {
            cgui.rotateHourLine();
        });

        root.getChildren().addAll(btn, btn2, btn3);
        Scene scene = new Scene(root, 300, 250);

        Timeline timeline = new Timeline();
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.getKeyFrames().add(
                new KeyFrame(Duration.seconds(1),
                        new EventHandler() {
                    // KeyFrame event handler
                    @Override
                    public void handle(Event event)
                    {
                        System.out.println(cgui.getRotateSecondLine());
                        cgui.rotateSecondLine();
                        if (cgui.getRotateSecondLine() >= 360) {
                            cgui.setRotateSecond(0);
                            cgui.rotateMinuteLine();
                        }
                        if (cgui.getRotateMinuteLine() >= 360) {
                            cgui.setRotateMinute(0);
                            cgui.rotateHourLine();
                        }
                        if (cgui.getRotateHourLine() >= 360) {
                            cgui.setRotateHour(0);
                        }
                    }
                }
                ));
        timeline.playFromStart();
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}
30.06.2017
Новые материалы

Введение в контекст React
В этом посте мы поговорим о Context API, который был представлен в React 16, и о том, как вы можете их использовать. Что такое контекст? Глядя на определение из react docs , оно..

Шлюз с лицензией OSS, совместимый с Apollo Federation v2, появится в WunderGraph
Сегодня мы рады сообщить, что мы сотрудничаем с поддерживаемой YC Tailor Technologies, Inc. для внедрения Apollo Federation v2. Реализация будет лицензирована MIT (Engine) и Apache 2.0..

Это оно
Ну, я официально уволился с работы! На этой неделе я буду лихорадочно выполнять последние требования Думающего , чтобы я мог сосредоточиться на поиске работы. Что именно это значит?..

7 полезных библиотек JavaScript, которые вы должны использовать в своем следующем проекте
Усильте свою разработку JavaScript Есть поговорка «Не нужно изобретать велосипед». Библиотеки — лучший тому пример. Это поможет вам написать сложные и трудоемкие функции простым способом...

Базовое руководство по переносу концепций обучения в глубокое обучение
Обзор По мере того, как машинное обучение становится все более мощным и продвинутым, модели, обеспечивающие эту расширенную возможность, становятся все больше и начинают требовать огромного..

C в C.R.U.D с использованием React-Redux
Если вы использовали React, возможно, вы знакомы с головной болью, связанной с обратным потоком данных. Передача состояния реквизитам от родительских компонентов к дочерним компонентам может..

5 обязательных элементов современного инструмента конвейера данных
В цифровом мире предприятия используют конвейеры данных для перемещения, преобразования и хранения огромных объемов данных. Эти конвейеры составляют основу бизнес-аналитики и играют..