2016-02-02 18 views
0

我已經創建了此方法來以幻燈片形式顯示圖像列表。 它工作正常,直到它有超過50或60個圖像處理。然後它崩潰,與我的方法正在耗盡堆空間。我需要設置爲空嗎?

java.lang.OutOfMemoryError: Java heap space

所以我不知道我是否應該設置一些每輪循環爲空?我在這裏和網上搜索,但沒有找到答案。

這裏的方法:是

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 
+0

也許你沒有足夠的圖像,你正在加載。注意:當你這樣做時,圖像將被解壓縮,因此將使用完整的未壓縮大小。例如一個2百萬像素的圖像可能會使用8 MB和32位顏色,如果您有60個圖像,則會使用1/2 GB。 –

+1

@PeterLawrey更接近1GB:在這個實現中每個圖像有兩個內存中表示,一個是'BufferedImage'和一個是JavaFX'圖像'。 –

回答

1

你的主要SequentialTransition加載的所有圖像,並保留對它們的引用開始播放之前。所以這種技術不會隨着圖像的數量而擴展(這意味着如果你有足夠的圖像,它將保證用盡你的堆空間)。

如果你的圖片來自BufferedImage小號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,因此一次迭代的最後一個關鍵幀與下一次循環的關鍵幀之間的插值將創建淡出。

如果圖像需要一些時間進行轉換,那麼由於圖像出現之前的延遲,此實現可能會顯示一些急動,這會破壞「淡入」效果。解決的辦法是使用一個後臺線程的圖像轉換,並放置在一個綁定的阻塞隊列:

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 s到列表被傳遞給該方法。這已經消耗了很多內存:在輸入方法體之前,基本上都是將所有圖像放在內存中。根據這些圖像來自何處,例如,您可以通過一個File對象列表,並在此處使用相同的技術根據需要隨時加載每個圖像(或創建一個小的隊列,如第二個例子)。這將基本上擴展到任意數量的圖像。

+0

這個答案提供了一個明確的解釋和解決方案。它還幫助我更多地瞭解KeyFrames和TimeLines,以及關於傳遞大量對象的信息。非常感謝@James_D :) – John