Сокращение времени сбора мусора

У меня есть приложение для воспроизведения 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;
}

Теперь сборщик мусора тратит менее 10% времени, обычно от 5% до 8%. И у меня все 30 видео воспроизводятся плавно одновременно.

Может ли изменение места (размещение объявления IVideoPicture снаружи и выделение памяти только один раз) быть проблемой? Будут ли временные метки изображения устанавливаться каждый раз, когда новое видеоизображение декодируется в выделенной памяти?

Спасибо


person nullptr    schedule 28.07.2013    source источник
comment
С какими параметрами вы сейчас запускаете свою java-программу?   -  person Emil H    schedule 28.07.2013
comment
Можете ли вы также включить показания из jstat -gcutil <PID>   -  person bsd    schedule 28.07.2013


Ответы (3)


Скорее всего, ваш текущий GC плохо подходит для вашей задачи. Чтобы получить предсказуемое время GC, вы можете попробовать использовать сборщик мусора G1 (я предполагаю, что вы используете Java 7u4 или более позднюю версию). G1 планируется в качестве долгосрочной замены для Concurrent Mark-Sweep Collector (CMS). При сравнении G1 с CMS есть отличия, которые делают G1 лучшим решением. G1 предлагает более предсказуемые паузы для сборки мусора, чем сборщик CMS, и позволяет пользователям указывать желаемые цели паузы.

Поэкспериментируйте со следующими параметрами, чтобы заархивировать наилучшую производительность для вашего конкретного случая:

-XX:+UseG1GC — указывает JVM использовать сборщик мусора G1.

-XX:MaxGCPauseMillis=500 — устанавливает цель для максимального времени паузы GC. Это мягкая цель, и JVM приложит все усилия для ее достижения. Таким образом, цель времени паузы иногда не будет достигнута. Значение по умолчанию — 200 миллисекунд.

-XX:InitiatingHeapOccupancyPercent=80 — Процент заполнения кучи для запуска параллельного цикла сборки мусора. Он используется G1 для запуска параллельного цикла GC на основе занятости всей кучи, а не только одного из поколений. Значение 0 означает «выполнять постоянные циклы GC». Значение по умолчанию — 45%.

Дополнительные сведения доступны здесь

person Jk1    schedule 28.07.2013

Одним из способов было бы изменить размер кучи и размер разных поколений. В документе GC от Oracle объясняется, как и тюнинг работает.

person morpheus05    schedule 28.07.2013

У меня есть такой совет:

  1. Никогда не используйте Thread.sleep(long), когда вам нужно очень точное время; используйте Thread.sleep(0,long). При этом используется наносекундная точность.

  2. Никогда не используйте Thread.sleep совсем для точно повторяющихся задач. Используйте ScheduledExecutorService и запланируйте задачу с точным интервалом, который вам нужен. Я лично был свидетелем того, как этот метод был более точным, чем метод сна в Windows.

  3. Просмотрите свой код, отметьте каждое место, где происходит выделение памяти, и тщательно подумайте + исследуйте, можно ли заменить выделение, просто заменив содержимое какого-либо существующего буфера памяти. Если вы уменьшите распределение, GC легко соберет то немногое, что осталось собрать.

person Marko Topolnik    schedule 28.07.2013