2013-07-28 111 views
3

我正在申請一次播放30個視頻。我使用Xuggler解碼視頻文件和Swing窗口進行顯示。減少垃圾收集時間

但我面對類似的原因:

  1. 影片沒有顯示流暢
  2. 探查器,我發現,25%左右的時間花費在垃圾收集。

如何調整垃圾回收器和其他性能參數,我應該注意什麼?

Xuggler-Java組合不好嗎?

編輯

我的視頻解碼循環是:

private boolean decodeStreams() throws Exception { 
    IPacket packet = IPacket.make(); 

    long firstTimestampInStream = Global.NO_PTS; 
    long systemClockStartTime = 0; 

    viewer.urlStatusUpdate(index, Globals.STATUS_PLAYING); 

    while (container.readNextPacket(packet) >= 0) { 
     if (stopPlaying) { 
      if (isStopPlaying(2)) { 
       return false; 
      } 
     } 

     if (packet.getStreamIndex() == videoStreamID) { 
      IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight()); 
      int offset = 0; 
      while (offset < packet.getSize()) { 
       int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset); 
       if (bytesDecoded < 0) { 
        throw new Exception("Got error on decoding video"); 
       } 
       offset += bytesDecoded; 
       if (picture.isComplete()) { 
        if (firstTimestampInStream == Global.NO_PTS) { 
         firstTimestampInStream = picture.getTimeStamp(); 
         systemClockStartTime = System.currentTimeMillis(); 
        } else { 
         long millisecondsToSleep = (
           ((picture.getTimeStamp() - firstTimestampInStream)/1000) 
           - (System.currentTimeMillis() - systemClockStartTime) 
           ); 
         if (millisecondsToSleep > 50) { 
          try { 
           Thread.sleep(millisecondsToSleep - 50); 
          } catch (Exception e) { 
          } 
         } 
        } 
        viewer.videoImageUpdate(index, converter.toImage(picture)); 
       } 
      } 
     } 
    } 

    return true; 
} 

我改變的地方IVideoPicture聲明:

private boolean decodeStreams() throws Exception { 
    IPacket packet = IPacket.make(); 

    long firstTimestampInStream = Global.NO_PTS; 
    long systemClockStartTime = 0; 

    viewer.urlStatusUpdate(index, Globals.STATUS_PLAYING); 
    IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight()); 

    while (container.readNextPacket(packet) >= 0) { 
     if (stopPlaying) { 
      if (isStopPlaying(2)) { 
       return false; 
      } 
     } 

     if (packet.getStreamIndex() == videoStreamID) { 
      int offset = 0; 
      while (offset < packet.getSize()) { 
       int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset); 
       if (bytesDecoded < 0) { 
        throw new Exception("Got error on decoding video"); 
       } 
       offset += bytesDecoded; 
       if (picture.isComplete()) { 
        if (firstTimestampInStream == Global.NO_PTS) { 
         firstTimestampInStream = picture.getTimeStamp(); 
         systemClockStartTime = System.currentTimeMillis(); 
        } else { 
         long millisecondsToSleep = (
           ((picture.getTimeStamp() - firstTimestampInStream)/1000) 
           - (System.currentTimeMillis() - systemClockStartTime) 
           ); 
         if (millisecondsToSleep > 50) { 
          try { 
           Thread.sleep(millisecondsToSleep - 50); 
          } catch (Exception e) { 
          } 
         } 
        } 
        viewer.videoImageUpdate(index, converter.toImage(picture)); 
       } 
      } 
     } 
    } 

    return true; 
} 

現在GC花費不到10%的時間,圍繞通常爲5%至8 &。我一次可以順利播放所有30個視頻。

正在改變的地方(把IVideoPicture聲明放在外面,並且只分配一次內存)會是問題嗎?每次新視頻圖像在分配的內存上解碼時,是否會設置圖像時間戳?

感謝

+0

用什麼參數,你現在運行的Java程序? –

+0

你是否也可以包含'jstat -gcutil ' – bsd

回答

3

有可能是您當前的GC不適合您的任務。爲了獲得可預測的GC時序,您可以嘗試使用G1垃圾回收器(我假設您使用的是Java 7u4或更高版本).G1計劃作爲併發標記掃描收集器(CMS)的長期替代品。比較G1和CMS,有一些差異可以使G1成爲更好的解決方案。 G1比CMS收集器提供更多可預測的垃圾收集暫停,並允許用戶指定所需的暫停目標。

播放使用下列選項歸檔的最佳性能爲您的具體情況:

-XX:+ UseG1GC - 告訴使用G1垃圾收集器的JVM。

-XX:MaxGCPauseMillis = 500 - 設置最大GC暫停時間的目標。這是一個柔和的目標,JVM將盡其最大努力來實現它。因此,暫停時間目標有時不會被滿足。默認值是200毫秒。

-XX:InitiatingHeapOccupancyPercent = 80 - 啓動併發GC循環的堆佔用率的百分比。它被G1用來根據整個堆的佔用情況來觸發一個並行的GC週期,而不僅僅是一代。值爲0表示'做常量GC循環'。默認值是45%。

提供更多細節here

0

我有這樣的建議:當你需要非常精確的定時

  1. 切勿使用Thread.sleep(long);使用Thread.sleep(0,long)。這使用納秒精度。

  2. 從來沒有使用Thread.sleep在所有精確重複的任務。使用ScheduledExecutorService並按您需要的精確時間間隔安排任務。我個人目睹這種方法比Windows上的睡眠方法更精確。

  3. 通過你的代碼,記下每個地方的內存分配發生的地方,並認爲科研+硬是否分配可以通過更換隻是一些現有的內存緩衝區的內容所代替。如果你減少分配,GC將很容易地收集剩下的收集資料。