ADDITIONAL SYSTEM INFORMATION :
Windows 11, JavaFX 25
A DESCRIPTION OF THE PROBLEM :
Referencing an old bug ticket, that was closed, but describes the same problem:JDK-8209876
Media files are not released by the JVM, even though stop and dispose is called on the JavaFX Media Player.
Workaround: Calling System.gc() after disposing, releases the file handle, otherwise the whole JVM would need to be shut down to relase the lock.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code below, klick the butons 1 to 5 to select a file, play it, dispose the player and try to rename and delete it.
---------- BEGIN SOURCE ----------
public class MediaPlayerFileHandleDemo extends Application {
private MediaPlayer player;
private File selectedFile;
private final TextArea log = new TextArea();
private final Label fileLabel = new Label("No file selected");
@Override
public void start(final Stage stage) {
this.log.setEditable(false);
this.log.setPrefRowCount(18);
final Button choose = new Button("1) Choose media file");
choose.setOnAction(e -> chooseFile(stage));
final Button createAndPlay = new Button("2) Create + Play (short)");
createAndPlay.setOnAction(e -> createAndPlayShort());
final Button stopDispose = new Button("3) Stop + Dispose");
stopDispose.setOnAction(e -> stopAndDispose());
final Button rename = new Button("4) Rename file (tests handle lock)");
rename.setOnAction(e -> tryRename());
final Button delete = new Button("5) Delete file (tests handle lock)");
delete.setOnAction(e -> tryDelete());
final HBox row1 = new HBox(10, choose, createAndPlay, stopDispose);
final HBox row2 = new HBox(10, rename, delete);
row1.setPadding(new Insets(10));
row2.setPadding(new Insets(10));
final VBox root = new VBox(10,
new VBox(5, new Label("Selected:"), this.fileLabel),
row1,
row2,
new Separator(),
new Label("Log:"),
this.log);
root.setPadding(new Insets(10));
stage.setScene(new Scene(root, 900, 500));
stage.setTitle("JavaFX MediaPlayer File Handle Demo");
stage.show();
stage.setOnCloseRequest(e -> {
safeStopDispose();
Platform.exit();
});
}
private void chooseFile(final Stage stage) {
final FileChooser fc = new FileChooser();
fc.setTitle("Pick an audio/video file (mp3/mp4/wav/...)");
final File f = fc.showOpenDialog(stage);
if (f != null) {
this.selectedFile = f;
this.fileLabel.setText(f.getAbsolutePath());
log("Selected: " + f);
}
}
private void createAndPlayShort() {
if (!ensureFile()) {
return;
}
safeStopDispose(); // clean up existing instance first
try {
final String uri = this.selectedFile.toURI().toString();
final Media media = new Media(uri);
this.player = new MediaPlayer(media);
this.player.setOnError(() -> log("MediaPlayer error: " + this.player.getError()));
media.setOnError(() -> log("Media error: " + media.getError()));
// Play briefly, then stop (to ensure pipeline actually opened the file)
this.player.play();
log("player.play() called");
// Stop after ~700ms (enough to trigger file open)
new Thread(() -> {
try {
Thread.sleep(700);
} catch (final InterruptedException ignored) {}
Platform.runLater(() -> {
if (this.player != null) {
this.player.stop();
log("player.stop() called (after short play)");
}
});
}, "StopLater").start();
} catch (final Exception ex) {
log("Create/play failed: " + ex);
}
}
private void stopAndDispose() {
if (this.player == null) {
log("No player to dispose");
return;
}
try {
this.player.stop();
log("player.stop()");
this.player.dispose();
log("player.dispose()");
this.player = null;
// System.gc();
// log("System.gc() requested");
} catch (final Exception ex) {
log("Stop/dispose failed: " + ex);
}
}
private void tryRename() {
if (!ensureFile()) {
return;
}
final Path p = this.selectedFile.toPath();
final Path target = p.resolveSibling(p.getFileName().toString() + ".renamed.tmp");
try {
// Ensure target not existing
Files.deleteIfExists(target);
Files.move(p, target, StandardCopyOption.ATOMIC_MOVE);
log("RENAME OK: " + p.getFileName() + " -> " + target.getFileName());
// move back
Files.move(target, p, StandardCopyOption.ATOMIC_MOVE);
log("Rename back OK");
} catch (final Exception ex) {
log("RENAME FAILED (possible handle lock): " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
private void tryDelete() {
if (!ensureFile()) {
return;
}
final Path p = this.selectedFile.toPath();
try {
Files.delete(p);
log("DELETE OK: " + p.getFileName());
this.selectedFile = null;
this.fileLabel.setText("No file selected");
} catch (final Exception ex) {
log("DELETE FAILED (possible handle lock): " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
private boolean ensureFile() {
if (this.selectedFile == null) {
log("Select a file first");
return false;
}
if (!this.selectedFile.exists()) {
log("Selected file no longer exists: " + this.selectedFile);
return false;
}
return true;
}
private void safeStopDispose() {
if (this.player != null) {
try {
this.player.stop();
} catch (final Exception ignored) {}
try {
this.player.dispose();
} catch (final Exception ignored) {}
this.player = null;
}
}
private void log(final String msg) {
final String line = "[" + LocalTime.now().withNano(0) + "] " + msg;
System.out.println(line);
this.log.appendText(line + "\n");
}
public static void main(final String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If there is a System.gc() call after dispose (see line 139), then steps 4 and 5 will work.
FREQUENCY :
ALWAYS
Windows 11, JavaFX 25
A DESCRIPTION OF THE PROBLEM :
Referencing an old bug ticket, that was closed, but describes the same problem:
Media files are not released by the JVM, even though stop and dispose is called on the JavaFX Media Player.
Workaround: Calling System.gc() after disposing, releases the file handle, otherwise the whole JVM would need to be shut down to relase the lock.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code below, klick the butons 1 to 5 to select a file, play it, dispose the player and try to rename and delete it.
---------- BEGIN SOURCE ----------
public class MediaPlayerFileHandleDemo extends Application {
private MediaPlayer player;
private File selectedFile;
private final TextArea log = new TextArea();
private final Label fileLabel = new Label("No file selected");
@Override
public void start(final Stage stage) {
this.log.setEditable(false);
this.log.setPrefRowCount(18);
final Button choose = new Button("1) Choose media file");
choose.setOnAction(e -> chooseFile(stage));
final Button createAndPlay = new Button("2) Create + Play (short)");
createAndPlay.setOnAction(e -> createAndPlayShort());
final Button stopDispose = new Button("3) Stop + Dispose");
stopDispose.setOnAction(e -> stopAndDispose());
final Button rename = new Button("4) Rename file (tests handle lock)");
rename.setOnAction(e -> tryRename());
final Button delete = new Button("5) Delete file (tests handle lock)");
delete.setOnAction(e -> tryDelete());
final HBox row1 = new HBox(10, choose, createAndPlay, stopDispose);
final HBox row2 = new HBox(10, rename, delete);
row1.setPadding(new Insets(10));
row2.setPadding(new Insets(10));
final VBox root = new VBox(10,
new VBox(5, new Label("Selected:"), this.fileLabel),
row1,
row2,
new Separator(),
new Label("Log:"),
this.log);
root.setPadding(new Insets(10));
stage.setScene(new Scene(root, 900, 500));
stage.setTitle("JavaFX MediaPlayer File Handle Demo");
stage.show();
stage.setOnCloseRequest(e -> {
safeStopDispose();
Platform.exit();
});
}
private void chooseFile(final Stage stage) {
final FileChooser fc = new FileChooser();
fc.setTitle("Pick an audio/video file (mp3/mp4/wav/...)");
final File f = fc.showOpenDialog(stage);
if (f != null) {
this.selectedFile = f;
this.fileLabel.setText(f.getAbsolutePath());
log("Selected: " + f);
}
}
private void createAndPlayShort() {
if (!ensureFile()) {
return;
}
safeStopDispose(); // clean up existing instance first
try {
final String uri = this.selectedFile.toURI().toString();
final Media media = new Media(uri);
this.player = new MediaPlayer(media);
this.player.setOnError(() -> log("MediaPlayer error: " + this.player.getError()));
media.setOnError(() -> log("Media error: " + media.getError()));
// Play briefly, then stop (to ensure pipeline actually opened the file)
this.player.play();
log("player.play() called");
// Stop after ~700ms (enough to trigger file open)
new Thread(() -> {
try {
Thread.sleep(700);
} catch (final InterruptedException ignored) {}
Platform.runLater(() -> {
if (this.player != null) {
this.player.stop();
log("player.stop() called (after short play)");
}
});
}, "StopLater").start();
} catch (final Exception ex) {
log("Create/play failed: " + ex);
}
}
private void stopAndDispose() {
if (this.player == null) {
log("No player to dispose");
return;
}
try {
this.player.stop();
log("player.stop()");
this.player.dispose();
log("player.dispose()");
this.player = null;
// System.gc();
// log("System.gc() requested");
} catch (final Exception ex) {
log("Stop/dispose failed: " + ex);
}
}
private void tryRename() {
if (!ensureFile()) {
return;
}
final Path p = this.selectedFile.toPath();
final Path target = p.resolveSibling(p.getFileName().toString() + ".renamed.tmp");
try {
// Ensure target not existing
Files.deleteIfExists(target);
Files.move(p, target, StandardCopyOption.ATOMIC_MOVE);
log("RENAME OK: " + p.getFileName() + " -> " + target.getFileName());
// move back
Files.move(target, p, StandardCopyOption.ATOMIC_MOVE);
log("Rename back OK");
} catch (final Exception ex) {
log("RENAME FAILED (possible handle lock): " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
private void tryDelete() {
if (!ensureFile()) {
return;
}
final Path p = this.selectedFile.toPath();
try {
Files.delete(p);
log("DELETE OK: " + p.getFileName());
this.selectedFile = null;
this.fileLabel.setText("No file selected");
} catch (final Exception ex) {
log("DELETE FAILED (possible handle lock): " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
private boolean ensureFile() {
if (this.selectedFile == null) {
log("Select a file first");
return false;
}
if (!this.selectedFile.exists()) {
log("Selected file no longer exists: " + this.selectedFile);
return false;
}
return true;
}
private void safeStopDispose() {
if (this.player != null) {
try {
this.player.stop();
} catch (final Exception ignored) {}
try {
this.player.dispose();
} catch (final Exception ignored) {}
this.player = null;
}
}
private void log(final String msg) {
final String line = "[" + LocalTime.now().withNano(0) + "] " + msg;
System.out.println(line);
this.log.appendText(line + "\n");
}
public static void main(final String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If there is a System.gc() call after dispose (see line 139), then steps 4 and 5 will work.
FREQUENCY :
ALWAYS
- relates to
-
JDK-8088566 MediaPlayer does not release file handle for MP3 files
-
- Open
-
-
JDK-8209876 Can not release file from MediaPlayer dispose() dosent work
-
- Closed
-