2013-07-28 156 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将很容易地收集剩下的收集资料。