Windows 10 上的 JavaFX 8 性能问题



>我有一个基本的JavaFX程序,由一个带有一些选项卡和几个按钮等的窗口组成。在OSX和Linux上,一切都很好,但是在Windows 10上,移动主窗口似乎给我的显卡带来了巨大的压力,每当我在应用程序的窗口和另一个窗口之间切换时,整个屏幕都会闪烁一秒钟。这只发生在这个应用程序上,该系统是一个高性能的游戏装备,具有G-Sync,980 GTX,i7-6700k,16 GB RAM,双显示器等,并且在各种CPU和GPU基准测试上表现非常好,真的,真的,在屏幕上移动窗口应该没有任何问题。

当我在任务管理器中观察 Java 进程时,RAM 使用率没有任何明显的变化,在屏幕上移动窗口时,CPU 使用率仅为 ~1.4%。拖动窗口时,它似乎只以大约 20 FPS 的速度更新,而移动系统上的任何其他窗口似乎以 60+ FPS 的速度运行。简而言之,存在某种与可视化渲染我的 JavaFX 窗口相关的奇怪的性能瓶颈。

据我所知,我没有使用任何3D功能或类似的东西,也没有做任何奇怪的JavaFX事情。我的窗口创建代码基于 Oracle 关于如何初始化 JavaFX 窗口等的标准教程。

下面是我的 JavaFX 窗口的初始化代码,以及控制器中的 init 方法:

Main.start():

@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.hide();
    lockInstance();
    Platform.setImplicitExit(false);
    hostServices = HostServicesFactory.getInstance(this);
    AccountManager.init();
    if(OS.isLinux()) {
        CURRENT_OS = OS_TYPE.LINUX;
    } else if(OS.isMacOS()) {
        CURRENT_OS = OS_TYPE.OSX;
    } else if(OS.isWindows()) {
        CURRENT_OS = OS_TYPE.WINDOWS;
    }
    System.out.println("detected os: " + CURRENT_OS);
    //AccountManager.urlPrefix = "http://localhost:8080/api/";
    String res = "ERROR";
    try {
        res = JWSystem.getAppBundleName();
    } catch (Exception e) {}
    if(!res.contains("ERROR"))
        DEPLOY_MODE = true;
    System.out.println("DEPLOY_MODE: " + DEPLOY_MODE);
    prefs = Preferences.userNodeForPackage(this.getClass());
    FIRST_RUN = prefs.getBoolean("first_run", true);
    prefs.putBoolean("first_run", false);
    System.out.println("FIRST_RUN: " + FIRST_RUN);
    if(FIRST_RUN && DEPLOY_MODE) {
        createDesktopIcon();
    }
    // setup
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            if(CURRENT_OS == OS_TYPE.LINUX)
                SystemIcons.initUbuntuMimeTypes();
            if(FIRST_RUN && DEPLOY_MODE) {
                addStartupEntry();
            }
        }
    });
    t.start();
    // load UI
    Parent root = FXMLLoader.load(getClass().getResource("assets/main.fxml"));
    primaryStage.resizableProperty().setValue(Boolean.FALSE);
    primaryStage.setTitle("DuroCloud");
    Scene scene = new Scene(root, 600, 400);
    mainScene = scene;
    primaryStage.setScene(scene);
    mainStage = primaryStage;
    scene.getStylesheets().addAll(getClass().getResource("assets/overrides.css").toExternalForm());
    primaryStage.show();
    primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
        public void handle(WindowEvent we) {
            Platform.exit();
            System.out.println("Stage is closing");
            System.exit(0);
        }
    });
}

Controller.initialize():

    public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
        faFolderIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-folder_256_0_464646_none.png"));
        faFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file_256_0_464646_none.png"));
        faTextFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-text_256_0_464646_none.png"));
        faPdfFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-pdf-o_256_0_464646_none.png"));
        faMovieFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-movie-o_256_0_464646_none.png"));
        faImageFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-image-o_256_0_464646_none.png"));
        faExcelFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-excel-o_256_0_464646_none.png"));
        faCodeFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-code-o_256_0_464646_none.png"));
        faAudioFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-audio-o_256_0_464646_none.png"));
        faArchiveFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-archive-o_256_0_464646_none.png"));
        // text files
        iconMap.put("txt", faTextFileIconImage);
        iconMap.put("doc", faTextFileIconImage);
        iconMap.put("docx", faTextFileIconImage);
        iconMap.put("rtf", faTextFileIconImage);
        iconMap.put("asc", faTextFileIconImage);
        iconMap.put("cfg", faTextFileIconImage);
        iconMap.put("log", faTextFileIconImage);
        iconMap.put("ini", faTextFileIconImage);
        iconMap.put("odt", faTextFileIconImage);
        // pdf
        iconMap.put("pdf", faPdfFileIconImage);
        // movie files
        String[] movie_formats = new String[] {"webm", "mkv", "flv", "vob", "ogv", "drc", "avi", "mov", "wmv", "yuv",
                "rm", "rmvb", "asf", "mp4", "m4p", "m4v", "mpg", "mp2", "mpeg", "mpe", "mpv", "svi",
                "3gp", "mxf", "roq", "nsv"};
        for(String format : movie_formats) {
            iconMap.put(format, faMovieFileIconImage);
        }
        // image files
        String[] image_formats = new String[] {"png", "bmp", "jpg", "jpeg", "tiff", "gif", "raw"};
        for(String format : image_formats) {
            iconMap.put(format, faImageFileIconImage);
        }
        // excel
        iconMap.put("xls", faExcelFileIconImage);
        iconMap.put("ods", faExcelFileIconImage);
        iconMap.put("xlsx", faExcelFileIconImage);
        // code
        iconMap.put("rb", faCodeFileIconImage);
        iconMap.put("js", faCodeFileIconImage);
        iconMap.put("html", faCodeFileIconImage);
        iconMap.put("htm", faCodeFileIconImage);
        iconMap.put("css", faCodeFileIconImage);
        iconMap.put("xml", faCodeFileIconImage);
        iconMap.put("php", faCodeFileIconImage);
        iconMap.put("exe", faCodeFileIconImage);
        iconMap.put("sh", faCodeFileIconImage);
        iconMap.put("py", faCodeFileIconImage);
        iconMap.put("bat", faCodeFileIconImage);
        iconMap.put("jar", faCodeFileIconImage);
        iconMap.put("java", faCodeFileIconImage);
        iconMap.put("c", faCodeFileIconImage);
        iconMap.put("cpp", faCodeFileIconImage);
        iconMap.put("h", faCodeFileIconImage);
        iconMap.put("makefile", faCodeFileIconImage);
        iconMap.put("make", faCodeFileIconImage);
        // audio
        iconMap.put("asf", faAudioFileIconImage);
        iconMap.put("mp3", faAudioFileIconImage);
        iconMap.put("flac", faAudioFileIconImage);
        iconMap.put("ogg", faAudioFileIconImage);
        iconMap.put("wav", faAudioFileIconImage);
        iconMap.put("wma", faAudioFileIconImage);
        iconMap.put("webm", faAudioFileIconImage);
        // archive
        iconMap.put("7z", faArchiveFileIconImage);
        iconMap.put("zip", faArchiveFileIconImage);
        iconMap.put("rar", faArchiveFileIconImage);
        iconMap.put("tar", faArchiveFileIconImage);
        iconMap.put("gz", faArchiveFileIconImage);
        iconMap.put("bz", faArchiveFileIconImage);

        VBox accountAltVbox = new VBox();
        HBox accountAltHbox = new HBox();
        accountAltHbox.setAlignment(Pos.CENTER);
        accountAltVbox.setLayoutX(60.0);
        accountAltVbox.setLayoutY(40.0);
        accountAltVbox.setSpacing(8);
        accountEmailLabel.setText("test");
        accountEmailLabel.setFont(new Font(18));
        accountAltContent.setPrefWidth(800);
        accountAltContent.setPrefHeight(800);
        accountAltContent.getChildren().add(accountAltVbox);
        accountAltVbox.getChildren().add(new Label("Logged in as:"));
        accountAltHbox.getChildren().add(accountEmailLabel);
        accountAltVbox.getChildren().add(accountAltHbox);
        accountAltVbox.getChildren().add(accountLogoutButton);
        UIFonts.setFontAwesomeGlyph(upButton, FontAwesomeIcon.LEVEL_UP);
        UIFonts.setFontAwesomeGlyph(refreshButton, FontAwesomeIcon.REFRESH);
        UIFonts.setFontAwesomeGlyph(openButton, FontAwesomeIcon.FOLDER_OPEN);
        UIFonts.setFontAwesomeGlyph(saveButton, FontAwesomeIcon.DOWNLOAD);
        UIFonts.setFontAwesomeGlyph(deleteButton, FontAwesomeIcon.TRASH);
        UIFonts.setFontAwesomeGlyph(addButton, FontAwesomeIcon.PLUS);
        UIFonts.setFontAwesomeGlyph(backupDirectoriesButton, FontAwesomeIcon.CLOUD);
        loginEmailField.setOnKeyPressed(this::handleEmailFieldEnter);
        loginPasswordField.setOnKeyPressed(this::handlePasswordFieldEnter);
        loginButton.setOnAction(this::handleLoginButtonAction);
        forgotPasswordLink.setOnAction(this::visitForgotPassword);
        accountLogoutButton.setOnAction(this::handleLogoutButton);
        refreshButton.setOnAction(this::handleRefreshButton);
        upButton.setOnAction(this::handleUpButton);
        addButton.setOnAction(this::handleAddButton);
        deleteButton.setOnAction(this::handleDeleteButton);
        fileBrowser.setCellFactory(new Callback<ListView<BrowserItem>, ListCell<BrowserItem>>() {
            @Override
            public ListCell<BrowserItem> call(ListView<BrowserItem> param) {
                return new BrowserItemCell();
            }
        });
        fileBrowser.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                if(event.getClickCount() == 2) {
                    BrowserItem selected = fileBrowser.getSelectionModel().getSelectedItem();
                    if(selected != null) {
                        if(selected.isFile) {
                            downloadSelected();
                        } else {
                            currentDirectoryId = selected.file.file_id;
                            refresh();
                        }
                    }
                }
            }
        });
        openButton.setDisable(true);
        saveButton.setDisable(true);
        deleteButton.setDisable(false);
        if(!AccountManager.loggedIn()) {
            mainTabPane.getSelectionModel().select(accountTab);
            optionsTab.setDisable(true);
            myFilesTab.setDisable(true);
        }
    }

因此,事实证明这是一个与线程相关的问题,与JavaFX无关,一旦我在应用程序中重新组织了一些东西,它就消失了。事实证明,每当拖动窗口时,都会调用列表视图上的更新方法,从而导致运行计算成本高昂的方法。有趣的是,性能差异仅在Windows 10系统上很明显,并且确实导致了一些严重的图形问题。

TLDR:不要在控制绘图代码中做计算成本高的事情,尤其是在 Windows 10 上。

最新更新