我正在尝试使用JavaFX中的TimeLine
来快速显示450个PNG文件,每80毫秒最好。我甚至不能在1.5秒内做到这一点。这些文件各约为60kb。在动画开始之前,文件被读取并存储在内存中
会发生什么:
GC每隔+ -30帧运行一次,动画完全停止
我尝试了什么:
SVGPath
SVGPath
转换为PNG并在原始动画中显示Image
对象并存储在PriorityQueue中。使用不同的ImageView
对象Image
ImageView
个对象,用setVisible(false)
填充所有对象,然后在动画运行时我只设置一个setVisible(true)
我尝试更改VM选项(我有超过10GB的空闲内存):
-Xms10680m -Xmx10680m -XX:PermSize=1256m -XX:MaxPermSize=1256m -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedOops -XX:NewSize=1200m -XX:MaxNewSize=1200m
- 升级虚拟机
下一步
我将尝试视口并查看是否仍然会激活CPU,因为它将是一个大对象,只是将视图移到它上面。希望它能解决问题
运行应用
我启动应用程序,加载模型,在开始动画之前,VM使用率为2.4GB,而不是指定的10GB。当我启动应用程序时,它将图像缓冲到队列中,内存为4.5GB,仍然不是10GB。当动画运行时,内存增加到5GB,实际上波动到低于5GB以上
Java版
java version" 1.8.0_40-ea" Java(TM)SE运行时环境(版本1.8.0_40-ea-b15) Java HotSpot(TM)64位服务器VM(版本25.40-b18,混合模式)
升级版Java
java版" 1.8.0_40" Java(TM)SE运行时环境(版本1.8.0_40-b26) Java HotSpot(TM)64位服务器VM(版本25.40-b25,混合模式)
现在它每40帧停止一次,而不是每30帧停止
GC日志
CMS: abort preclean due to time 232.460: [CMS-concurrent-abortable-preclean: 1.017/97.038 secs] [Times: user=28.11 sys=2.54, real=97.04 secs]
232.463: [GC (CMS Final Remark) [YG occupancy: 649460 K (1105920 K)]232.463: [Rescan (parallel) , 0.1360990 secs]232.599: [weak refs processing, 0.0000293 secs]232.599: [class unloading, 0.0278749 secs]232.627: [scrub symbol table, 0.0024514 secs]232.630: [scrub string table, 0.0006221 secs][1 CMS-remark: 1010091K(9707520K)] 1659552K(10813440K), 0.1735120 secs] [Times: user=0.66 sys=0.00, real=0.17 secs]
232.637: [CMS-concurrent-sweep-start]
236.988: [GC (Allocation Failure) 236.988: [ParNew: 1105126K->122880K(1105920K), 0.5563841 secs] 2115195K->2064334K(10813440K), 0.5564532 secs] [Times: user=2.64 sys=0.66, real=0.56 secs]
242.032: [CMS-concurrent-sweep: 0.760/9.395 secs] [Times: user=13.48 sys=1.01, real=9.39 secs]
242.032: [CMS-concurrent-reset-start]
242.094: [CMS-concurrent-reset: 0.062/0.062 secs] [Times: user=0.09 sys=0.06, real=0.06 secs]
246.489: [GC (Allocation Failure) 246.489: [ParNew: 1103611K->122880K(1105920K), 0.4099299 secs] 3045066K->2974070K(10813440K) icms_dc=6 , 0.4100021 secs] [Times: user=1.12 sys=1.36, real=0.41 secs]
253.224: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2851190K(9707520K)] 3454329K(10813440K), 0.0044382 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
253.229: [CMS-concurrent-mark-start]
255.071: [CMS-concurrent-mark: 1.842/1.843 secs] [Times: user=3.84 sys=0.02, real=1.84 secs]
255.071: [CMS-concurrent-preclean-start]
255.103: [CMS-concurrent-preclean: 0.031/0.031 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
255.103: [CMS-concurrent-abortable-preclean-start]
CMS: abort preclean due to time 260.113: [CMS-concurrent-abortable-preclean: 1.737/5.011 secs] [Times: user=3.28 sys=0.05, real=5.01 secs]
260.114: [GC (CMS Final Remark) [YG occupancy: 603138 K (1105920 K)]260.114: [Rescan (parallel) , 0.0107781 secs]260.125: [weak refs processing, 0.0000543 secs]260.125: [class unloading, 0.0321900 secs]260.157: [scrub symbol table, 0.0024284 secs]260.159: [scrub string table, 0.0005998 secs][1 CMS-remark: 2851190K(9707520K)] 3454329K(10813440K), 0.0517110 secs] [Times: user=0.09 sys=0.02, real=0.05 secs]
260.166: [CMS-concurrent-sweep-start]
260.932: [CMS-concurrent-sweep: 0.767/0.767 secs] [Times: user=0.78 sys=0.02, real=0.77 secs]
260.932: [CMS-concurrent-reset-start]
260.950: [CMS-concurrent-reset: 0.018/0.018 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
264.709: [Full GC (System.gc()) 264.709: [CMS: 2850762K->3328314K(9707520K), 6.1267248 secs] 3453901K->3328314K(10813440K), [Metaspace: 27214K->27214K(1073152K)] icms_dc=2 , 6.1282785 secs] [Times: user=5.93 sys=0.16, real=6.13 secs]
270.838: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3328314K(9707520K)] 3341357K(10813440K), 0.0008962 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
270.839: [CMS-concurrent-mark-start]
282.708: [Full GC (System.gc()) 282.708: [CMS284.474: [CMS-concurrent-mark: 1.765/13.635 secs] [Times: user=4.57 sys=0.13, real=13.63 secs]
(concurrent mode interrupted): 3328314K->3328357K(9707520K), 7.6842704 secs] 3364891K->3328357K(10813440K), [Metaspace: 27270K->27270K(1073152K)] icms_dc=7 , 7.6858220 secs] [Times: user=9.39 sys=0.01, real=7.69 secs]
302.308: [Full GC (System.gc()) 302.308: [CMS: 3328357K->3328494K(9707520K), 6.0054401 secs] 3362268K->3328494K(10813440K), [Metaspace: 27306K->27306K(1073152K)] icms_dc=7 , 6.0070951 secs] [Times: user=5.98 sys=0.02, real=6.01 secs]
308.316: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3328494K(9707520K)] 3328494K(10813440K), 0.0008023 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
308.317: [CMS-concurrent-mark-start]
320.308: [Full GC (System.gc()) 320.308: [CMS322.084: [CMS-concurrent-mark: 1.777/13.768 secs] [Times: user=4.10 sys=0.13, real=13.77 secs]
(concurrent mode interrupted): 3328494K->3328612K(9707520K), 7.1761016 secs] 3374259K->3328612K(10813440K), [Metaspace: 27340K->27340K(1073152K)] icms_dc=7 , 7.1777096 secs] [Times: user=8.91 sys=0.02, real=7.18 secs]
339.108: [Full GC (System.gc()) 339.108: [CMS: 3328612K->3328734K(9707520K), 4.7149083 secs] 3381859K->3328734K(10813440K), [Metaspace: 27356K->27356K(1073152K)] icms_dc=7 , 4.7166533 secs] [Times: user=4.71 sys=0.00, real=4.72 secs]
343.825: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3328734K(9707520K)] 3338934K(10813440K), 0.0005971 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
343.826: [CMS-concurrent-mark-start]
355.908: [Full GC (System.gc()) 355.908: [CMS357.692: [CMS-concurrent-mark: 1.785/13.866 secs] [Times: user=4.26 sys=0.11, real=13.87 secs]
(concurrent mode interrupted): 3328734K->3328845K(9707520K), 6.5141915 secs] 3395891K->3328845K(10813440K), [Metaspace: 27376K->27376K(1073152K)] icms_dc=7 , 6.5157445 secs] [Times: user=8.25 sys=0.03, real=6.52 secs]
374.308: [Full GC (System.gc()) 374.308: [CMS: 3328845K->3328969K(9707520K), 5.1376448 secs] 3399645K->3328969K(10813440K), [Metaspace: 27393K->27393K(1073152K)] icms_dc=7 , 5.1391948 secs] [Times: user=5.13 sys=0.00, real=5.14 secs]
379.447: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3328969K(9707520K)] 3339169K(10813440K), 0.0006191 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
379.448: [CMS-concurrent-mark-start]
391.907: [Full GC (System.gc()) 391.907: [CMS393.678: [CMS-concurrent-mark: 1.772/14.231 secs] [Times: user=4.31 sys=0.14, real=14.23 secs]
(concurrent mode interrupted): 3328969K->3329043K(9707520K), 7.0341647 secs] 3407161K->3329043K(10813440K), [Metaspace: 27454K->27454K(1073152K)] icms_dc=7 , 7.0357178 secs] [Times: user=8.75 sys=0.05, real=7.04 secs]
411.910: [Full GC (System.gc()) 411.910: [CMS: 3329043K->3329171K(9707520K), 5.7665632 secs] 3426534K->3329171K(10813440K), [Metaspace: 27477K->27477K(1073152K)] icms_dc=7 , 5.7681657 secs] [Times: user=5.77 sys=0.00, real=5.77 secs]
417.678: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3329171K(9707520K)] 3329171K(10813440K), 0.0008204 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
417.679: [CMS-concurrent-mark-start]
排队填充
我使用Executors填充队列
executorService = Executors.newFixedThreadPool(LAYER_PRODUCERS);
for (int i = -0; i < totalLayers; i++) {
layerProducer = new LayerProducer(queueImage, i);
executorService.submit(layerProducer);
}
等待队列填充然后开始动画
我等待线程填充队列然后我用Platform.runLater()启动动画,因为它需要在屏幕上运行Thread
new Thread() {
@Override
public void run() {
try {
while (true || executorService.isShutdown()) {
Thread.sleep(1000);
if (queueImage.size() >= (bufferSizeCalculated - 1)) {
break;
}
}
Platform.runLater(() -> {
runPrintThread();
});
} catch (InterruptedException ex) {
Logger.getLogger(PrintingEngine.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
动画循环
private void configureTimeLineContinuousNoBuildPlateMoveAnimation(Profile profile) {
timeLocation = 0;
List<KeyFrame> list = new ArrayList<>();
ObservableList<KeyFrame> observableList = FXCollections.observableList(list);
//create first frame
KeyFrame firstKeyFrame = new KeyFrame(Duration.millis(timeLocation), (ActionEvent t) -> {
GizmoSlice gizmoSlice = queueImage.poll();
ImageView currentImageView = gizmoSlice.getFilledImage();
currentImageView.setVisible(true);
previousImageView = currentImageView;
printEngineCallback.updateLog("Display Layer " + gizmoSlice.getName());
});
observableList.add(firstKeyFrame);
for (int i = 0; i < totalLayers; i++) {
//create a keyframe for each layer
KeyFrame keyFrame = new KeyFrame(Duration.millis(timeLocation = timeLocation + projectionTime), (ActionEvent t) -> {
previousImageView.setVisible(false);
GizmoSlice gizmoSlice = queueImage.poll();
ImageView currentImageView = gizmoSlice.getFilledImage();
currentImageView.setVisible(true);
previousImageView = currentImageView;
printEngineCallback.updateLog("Display Layer " + gizmoSlice.getName());
});
observableList.add(keyFrame);
}
engineTimeLine = TimelineBuilder.create()
.cycleCount(1)
.keyFrames(list)
.autoReverse(false).build();
}
测试代码
我写了一个单独的应用程序执行此操作,CPU根本没有尖峰。所以代码可能很好,VM可能很好。不知道问题可能是什么。没有其他东西在后台运行,它只是一个大型的JavaFX客户端应用程序
package au.com.gizmo3dprinters;
import static au.com.gizmo3dprinters.Constants.GIZMO_SLICE;
import static au.com.gizmo3dprinters.Constants.TEMP_DIRECTORY;
import au.com.gizmo3dprinters.model.GizmoSlice;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.TimelineBuilder;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.util.Duration;
/**
*
* @author kobus
*/
public class FXMLDocumentController implements Initializable {
private static final int BUFFER_SIZE = 1; //larger number is a smaller buffer
private static final int LAYER_PRODUCERS = 3; //larger is more memory and cpu power
private int totalLayers;
private PriorityQueue<GizmoSlice> queueImage;
private int projectionTime;
private Timeline engineTimeLine;
@FXML
private ImageView sliceImageView;
@FXML
private Label label;
@FXML
private void handleButtonAction(ActionEvent event) {
Comparator<GizmoSlice> comparator = new GizmoSliceComparator();
double bufferSizeCalculated = totalLayers / BUFFER_SIZE;
queueImage = new PriorityQueue(Double.valueOf(bufferSizeCalculated).intValue(), comparator);
String gizmoName = "noname_1428817659204.gizmo";
ExecutorService executorService = Executors.newFixedThreadPool(LAYER_PRODUCERS);
for (int i = -0; i < totalLayers; i++) {
LayerProducer layerProducer = new LayerProducer(queueImage, i, gizmoName);
executorService.submit(layerProducer);
}
executorService.shutdown();
try {
executorService.awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
configureTimeLineContinuousNoBuildPlateMoveAnimation();
engineTimeLine.playFromStart();
}
private void configureTimeLineContinuousNoBuildPlateMoveAnimation() {
long timeLocation = 0;
List<KeyFrame> list = new ArrayList<>();
ObservableList<KeyFrame> observableList = FXCollections.observableList(list);
//create first frame
KeyFrame firstKeyFrame = new KeyFrame(Duration.millis(timeLocation), (ActionEvent t) -> {
GizmoSlice gizmoSlice = queueImage.poll();
System.out.println("display layer " + gizmoSlice.getLayer());
Image currentImage = gizmoSlice.getFilledImage();
sliceImageView.setImage(currentImage);
});
observableList.add(firstKeyFrame);
for (int i = 0; i < totalLayers; i++) {
//create a keyframe for each layer
KeyFrame keyFrame = new KeyFrame(Duration.millis(timeLocation = timeLocation + projectionTime), (ActionEvent t) -> {
GizmoSlice gizmoSlice = queueImage.poll();
System.out.println("display layer " + gizmoSlice.getLayer());
Image currentImage = gizmoSlice.getFilledImage();
sliceImageView.setImage(currentImage);
});
observableList.add(keyFrame);
}
engineTimeLine = TimelineBuilder.create()
.cycleCount(1)
.keyFrames(list)
.autoReverse(false).build();
}
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
totalLayers = 480;
projectionTime = 400;
}
class LayerProducer implements Runnable {
private final PriorityQueue<GizmoSlice> queueImage;
private boolean stop = false;
private final int layer;
private final String gizmoName;
public void stopProducing() {
stop = true;
}
public boolean isStopped() {
return stop;
}
public LayerProducer(PriorityQueue<GizmoSlice> queueImage, int layer, String gizmoName) {
this.queueImage = queueImage;
this.layer = layer;
this.gizmoName = gizmoName;
}
@Override
public void run() {
String imageFilename = gizmoName + "fill" + layer + "." + GIZMO_SLICE + ".png";
File imageFile = new File(TEMP_DIRECTORY + imageFilename);
Image img = new Image(imageFile.toURI().toString());
ImageView imageView = new ImageView(img);
imageView.setVisible(false);
GizmoSlice gizmoSlice = new GizmoSlice();
gizmoSlice.setName(imageFilename);
gizmoSlice.setFilledImage(img);
gizmoSlice.setFilledImageView(imageView);
gizmoSlice.setLayer(layer);
queueImage.add(gizmoSlice);
}
}
public class GizmoSliceComparator implements Comparator<GizmoSlice> {
@Override
public int compare(GizmoSlice x, GizmoSlice y) {
Long layerX = x.getLayer();
Long layerY = y.getLayer();
return layerX.compareTo(layerY);
}
}
}
我仍然在研究这个问题。
我更改了代码以创建一个队列来读取图像的字节,然后是第二个较小的队列,其中包含转换为图像的字节,但处理过多,内存太多。
我想我也弄清楚我之前的问题是CPU峰值。我给VM 10GB的内存,垃圾收集造成了问题。
我也尝试过堆外内存,但所有工具都需要序列化对象。我试图序列化,但它太慢了。
我现在正在使用ffmpeg(Generate video with ffmpeg to play using JavaFX)从图像文件生成一部电影,但我无法弄清楚如何让ffmpeg生成JavaFX可以显示的电影。昨晚我尝试使用代码在JavaFX中使用VLC查看器并显示所有生成的视频。只是不喜欢使用特定于操作系统的代码。此外,它使用50%CPU和8核i7,因此它可能无法在Raspberry PI上运行。我需要做更多的测试,看看我是否可以将电影的图像与ImageView对象的图像对齐