我的方法是耗尽堆空间。我需要将某些内容设置为null吗?

时间:2016-02-02 01:54:13

标签: java animation javafx

我已创建此方法以幻灯片形式显示图像列表。 它工作正常,直到它有超过50或60个图像来处理。然后用

崩溃了

java.lang.OutOfMemoryError: Java heap space

所以我想知道每次循环时我是否应该设置为null?我在这里和网上搜索过,但还没有找到答案。

以下是方法:

private void createSlideshow(Stage stage, ArrayList<BufferedImage> slideList, int durationInSecs) throws InterruptedException {

            stage.show();
            SequentialTransition slideshow = new SequentialTransition();
            int i = 0;
            for (BufferedImage bi: slideList) {
                System.out.println(" Iteration " + (i++));
                ImageView slide = new ImageView(SwingFXUtils.toFXImage(bi, null));    // LINE 108
                FadeTransition fadeIn =  new FadeTransition(Duration.millis(durationInSecs * 1000), slide);
                fadeIn.setFromValue(0.0);
                fadeIn.setToValue(1.0);

                PauseTransition stayOn = new PauseTransition(Duration.millis(durationInSecs * 1000));

                FadeTransition fadeOut =  new FadeTransition(Duration.millis(durationInSecs * 1000), slide);
                fadeOut.setFromValue(1.0);
                fadeOut.setToValue(0.0);

                SequentialTransition fadeInOut = new SequentialTransition();
                fadeInOut.getChildren().addAll(fadeIn, stayOn, fadeOut);
                slide.setOpacity(0.0);              
                root.getChildren().add(slide);            
                slideshow.getChildren().add(fadeInOut);
            }
            slideshow.play();
        }

完整的运行时消息如下:

Exception in Application start method
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
        at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.nio.HeapByteBuffer.<init>(Unknown Source)
        at java.nio.ByteBuffer.allocate(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.createPlatformImage(Unknown Source)
        at javafx.scene.image.Image.<init>(Unknown Source)
        at javafx.scene.image.WritableImage.<init>(Unknown Source)
        at javafx.embed.swing.SwingFXUtils.toFXImage(Unknown Source)
        at Slideshow.createSlideshow(Slideshow.java:108)
        at Slideshow.start(Slideshow.java:52)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(Unknown Source)
        at com.sun.javafx.application.LauncherImpl$$Lambda$64/1581649247.run(Unknown Source)
        at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(Unknown Source)
        at com.sun.javafx.application.PlatformImpl$$Lambda$49/1915503092.run(Unknown Source)
        at com.sun.javafx.application.PlatformImpl.lambda$null$173(Unknown Source)
        at com.sun.javafx.application.PlatformImpl$$Lambda$51/1557268138.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(Unknown Source)
        at com.sun.javafx.application.PlatformImpl$$Lambda$50/1567581361.run(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
        at com.sun.glass.ui.win.WinApplication$$Lambda$39/1645995473.run(Unknown Source)
        ... 1 more
Exception running application Slideshow

1 个答案:

答案 0 :(得分:2)

您的主SequentialTransition正在加载所有图片并在开始播放之前保留对它们的引用。因此,这种技术不会随着图像的数量而扩展(这意味着如果你有足够的图像,它将保证耗尽你的堆空间)。

如果您的图片合理快速地从BufferedImage转换为FX Image,您可以使用时间轴执行此操作:

private void createSlideshow(Stage stage, ArrayList<BufferedImage> slideList, int durationInSecs) throws InterruptedException {

    stage.show();
    Timeline slideshow = new Timeline();

    ImageView slide = new ImageView();

    for (int i = 0; i < slideList.size(); i++) {
        BufferedImage bi = slideList.get(i);
        KeyFrame newImageFrame = new KeyFrame(Duration.seconds(durationInSeconds * 3 * i), e -> 
            slide.setImage(SwingFXUtils.toFXImage(bi, null)));

        KeyFrame startFadeIn = new KeyFrame(Duration.seconds(durationInSeconds * 3 * i), 
            new KeyValue(slide.opacityProperty(), 0));

        KeyFrame endFadeIn = new KeyFrame(Duration.seconds(durationsInSeconds * (3 * i + 1)), 
            new KeyValue(slide.opacityProperty(), 1));

        KeyFrame startFadeOut = new KeyFrame(Duration.seconds(durationInSeconds * (3 * i + 2)), 
            new KeyValue(slide.opacityProperty(), 1));

        slideshow.getKeyFrames().addAll(newImageFrame, startFadeIn, endFadeIn, startFadeOut);

    }
    slideshow.play();
}

在此实现中,您将为每个图像创建一个包含四个关键帧的时间轴。第一个将当前BufferedImage转换为JavaFX Image,并且下一个注册(在同一时间点)将不透明度设置为零。下一个关键帧的不透明度设置为1(因此时间轴将插入这两个关键帧之间的不透明度)。最终关键帧的不透明度也为1,因此它将在时间轴的那一部分保持不变。在循环的下一次迭代中,添加一个新的关键帧,不透明度为0,因此一次迭代的最后一个关键帧与循环的下一次迭代的关键帧之间的插值将产生淡出。

如果图像需要一些时间进行转换,那么此实现可能会因图像出现之前的延迟而显示出一些急动,从而破坏了#34;淡入&#34;淡入淡出影响。解决这个问题的方法是使用后台线程转换图像并将它们放在绑定的阻塞队列中:

private void createSlideshow(Stage stage, ArrayList<BufferedImage> slideList, int durationInSecs) throws InterruptedException {

    stage.show();

    int numImages = slideList.size();

    BlockingQueue<Image> images = new ArrayBlockingQueue<>(3);

    Thread conversionThread = new Thread(() -> {
        for (BufferedImage bi : slideList) {
            try {
                images.put(SwingFXUtils.toFXImage(bi, null));
            } catch (InterruptedException exc) {
                Thread.currentThread().interrupt();
            }
        }
    });
    conversionThread.setDaemon(true);
    conversionThread.start();

    Timeline slideshow = new Timeline();

    ImageView slide = new ImageView();

    for (int i = 0; i < slideList.size(); i++) {

        KeyFrame newImageFrame = new KeyFrame(Duration.seconds(durationInSeconds * 3 * i), e -> 
            slide.setImage(images.poll())); 

        KeyFrame startFadeIn = new KeyFrame(Duration.seconds(durationInSeconds * 3 * i), 
            new KeyValue(slide.opacityProperty(), 0));

        KeyFrame endFadeIn = new KeyFrame(Duration.seconds(durationsInSeconds * (3 * i + 1)), 
            new KeyValue(slide.opacityProperty(), 1));

        KeyFrame startFadeOut = new KeyFrame(Duration.seconds(durationInSeconds * (3 * i + 2)), 
            new KeyValue(slide.opacityProperty(), 1));

        slideshow.getKeyFrames().addAll(newImageFrame, startFadeIn, endFadeIn, startFadeOut);

    }
    slideshow.play();
}

最后请注意,您的原始代码以及这两个实现都希望将BufferedImage的列表传递给该方法。这已占用大量内存:在进入方法体之前,基本上是将所有图像保存在内存中。根据这些图像的来源,您可以通过一个File对象列表并在此处使用相同的技术根据需要动态加载每个图像(或创建一个小队列)他们在第二个例子中)。这将基本上扩展到任意数量的图像。