FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) Client VM (build 25.45-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 7 x64, Windows 8.1
A DESCRIPTION OF THE PROBLEM :
There is a race condition when disposing instances of com.sun.media.jfxmediaimpl.NativeMediaPlayer$EventQueueThread that prevents the thread from ever terminating.
The main culprit of the bug is in com.sun.media.jfxmediaimple.NativeMediaPlayer:1337-1341]. The event queue being processed by the leaking thread is cleared, however, this has the potential to remove the event that was added to the queue to notify the thread to stop, as a result it will hang forever on the take call. It needs to ensure the event queue has finished executing before clearing the queue.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached application, with multiple video files for an extended period of time, since this is a race condition reproduction depends on the machines performance.
If you're impatient just set a single thread breakpoint on the EventQueueThread take call(NativeMediaPlayer:397), or add a sleep call immediately before it and the threads will leak almost every time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
At most one JFXMedia Player EventQueueThread, since only one media object is ever playing at once in the sample application.
ACTUAL -
Hundreds of threads with a stack trace like:
JFXMedia Player EventQueueThread tid=4720257 [WAITING] [DAEMON]
sun.misc.Unsafe.park(boolean, long)
java.util.concurrent.locks.LockSupport.park(Object)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await()
java.util.concurrent.LinkedBlockingQueue.take()
com.sun.media.jfxmediaimpl.NativeMediaPlayer$EventQueueThread.run()
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Eventually you encounter an out of memory error:
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - java.lang.OutOfMemoryError
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at sun.misc.Unsafe.allocateMemory(Native Method)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.nio.DirectByteBuffer.<init>(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.nio.ByteBuffer.allocateDirect(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.prism.impl.BufferUtil.newByteBuffer(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.prism.impl.BufferUtil.newIntBuffer(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.prism.impl.QueuedPixelSource.getUnusedPixels(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.javafx.tk.quantum.UploadingPainter.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.javafx.tk.RenderJob.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(Unknown Source)
2015-06-26 01:38:56,372 [QuantumRenderer-0] E java.lang.Throwable - at java.lang.Thread.run(Unknown Source)
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.File;
import java.util.List;
public class MediaThreadLeak extends Application
{
private final List<File> files = FXCollections.observableArrayList( new File("C:/projects/t1.mp4"), new File("C:/projects/t2.mp4"));
private final MediaView mv = new MediaView();
private int mpi = -1;
@Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setTitle("Media Test");
Group root = new Group();
root.getChildren().add(mv);
Scene scene = new Scene(root, 500, 500, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
Timeline tl = new Timeline( new KeyFrame(Duration.millis(2000),e->changeMedia()));
tl.setCycleCount(Timeline.INDEFINITE);
tl.playFromStart();
}
private void changeMedia()
{
MediaPlayer mp = mv.getMediaPlayer();
if( mp != null)
{
mv.setMediaPlayer(null);
mp.stop();
mp.dispose();
}
mpi = ++mpi >= files.size() ? 0 : mpi;
mv.setMediaPlayer(new MediaPlayer(new Media(files.get(mpi).toURI().toString())));
mv.getMediaPlayer().setAutoPlay(true);
mv.setFitHeight(800);
mv.setFitWidth(800);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I see no workaround aside from modifying the javafx code or restarting the application periodically.