减少垃圾收集时间

时间:2013-07-28 09:20:50

标签: java swing garbage-collection xuggler

我正在申请一次播放30个视频。我使用Xuggler解码视频文件和Swing窗口进行显示。

但我遇到的问题如下:

  1. 视频无法显示
  2. 我发现使用分析器,大约有25%的时间用于垃圾收集。
  3. 如何调整垃圾收集器和其他性能参数,我应该注意什么?

    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&amp;一般。我一次播放了所有30个视频。

    更改位置(将IVideoPicture声明放在外面,只分配一次内存)可能有问题吗?每次在分配的内存上解码新的视频图像时,图像集的时间戳会被设置吗?

    由于

3 个答案:

答案 0 :(得分:3)

您当前的GC不适合您的任务。为了获得可预测的GC时序,您可以尝试使用G1垃圾收集器(我假设您使用的是Java 7u4或更高版本).G1计划作为并发标记扫描收集器(CMS)的长期替代品。将G1与CMS进行比较,存在差异,使G1成为更好的解决方案。 G1提供比CMS收集器更可预测的垃圾收集暂停,并允许用户指定所需的暂停目标。

使用以下选项来存档针对您的特定情况的最佳效果:

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

-XX:MaxGCPauseMillis = 500 - 设置最大GC暂停时间的目标。这是一个软目标,JVM将尽最大努力实现它。因此,有时不会达到暂停时间目标。默认值为200毫秒。

-XX:InitiatingHeapOccupancyPercent = 80 - 启动并发GC循环的堆占用百分比。 G1使用它来根据整个堆的占用率触发并发GC循环,而不仅仅是其中一代。值0表示“执行恒定GC循环”。默认值为45%。

更多详细信息here

答案 1 :(得分:2)

一种方法是更改​​堆大小和不同代的大小。 GC Papre from Oracle解释了gc和调优的工作原理。

答案 2 :(得分:0)

我有这个建议:

  1. 当您需要非常精确的计时时,切勿使用Thread.sleep(long);使用Thread.sleep(0,long)。这使用纳秒精度。

  2. 永远不要使用Thread.sleep 来精确重复任务。使用ScheduledExecutorService并按照您需要的精确时间间隔安排任务。我亲眼目睹了这种方法比Windows上的睡眠方法更精确。

  3. 浏览你的代码,记下发生内存分配的每个地方,并思考+研究是否可以通过替换某些现有内存缓冲区的内容来替换分配。如果你取消分配,GC将很容易收集剩下的东西来收集。