sshahine / JFoenix

JavaFX Material Design Library
MIT License
6.29k stars 1.06k forks source link

JFXSpinner can cause memory leaks #1181

Closed RationalityFrontline closed 2 years ago

RationalityFrontline commented 3 years ago

Place a JFXSpinner in the stage, then close the stage, then call System.gc(), however the closed stage won't get garbage collected. This is because JFXSpinner uses custom skin (JFXSpinnerSkin), and the skin won't get its timeline based animation paused or cleared when the stage get closed.

JFoenix Version: 9.0.10

Test code (Kotlin) :

import com.jfoenix.controls.JFXSpinner
import javafx.application.Application
import javafx.geometry.Pos
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.layout.StackPane
import javafx.scene.layout.VBox
import javafx.stage.Stage
import java.lang.ref.WeakReference

class TestApplication : Application() {
    companion object {
        val STAGES = mutableListOf<WeakReference<Stage>>()
    }

    override fun start(primaryStage: Stage) {
        initAndShowStage(primaryStage)
    }

    private fun initAndShowStage(stage: Stage) {
        STAGES.add(WeakReference(stage))
        val spinner = JFXSpinner()
        val scene = Scene(StackPane(VBox(20.0).apply {
            prefWidth = 400.0
            prefHeight = 300.0
            alignment = Pos.CENTER
            children.addAll(
                spinner,
                Button("print stages").apply {
                    setOnAction {
                        System.gc()
                        STAGES.removeAll { it.get() == null }
                        println(STAGES)
                    }
                },
                Button("reload stage").apply {
                    setOnAction {
                        initAndShowStage(Stage())
                        stage.close()
                    }
                }
            )
        }))
        stage.scene = scene
//        stage.setOnHidden { spinner.skin.dispose() }
        stage.show()
    }
}

fun main() {
    Application.launch(TestApplication::class.java)
}