Primary tabs

Моя любимая ошибка: ошибка сегментации в Java

Перевод статьи My favorite bug: segfaults in Java с любезного согласия Luke Shumaker

Я рассказал эту историю устно несколько раз, но понял, что я никогда не писал об этом. Это моя любимая история; возможно, она не о моей грубейшей ошибке, но это то, о чем я больше всего хотел сказать.

Контекст

В 2012 году я был старшим программистом в команде FIRST Robotics Competition 1024. знакомы, соответствующая часть установки является то, что есть 2 минуты и 15 секунд матча, в которых у вас есть 120 фунтов робота, который иногда работает автономно, и иногда контролируется по WiFi от человека на ноутбуке под управлением фондового программного обеспечения "драйвера станция" и изменяемой программное обеспечение "приборной панели".

В том же году, мы в основном использовали программное обеспечение приборной панели, чтобы позволяют Датчики контролируют на роботе, один из них видео-канал от установленного на нем веб-камерой. Это было действительно легко, потому что новая стандартная программа приборной панели был интерфейс, нажмите и перетащите, чтобы добавить акции виджеты; Вы только должны были убедиться, что код на робота, который фактически отправляет данные.

Это здорово, пока при отладке вещи, приборная панель не вдруг исчезают. Если он был запущен вручную с терминала (вместо того, чтобы запуск драйвера станции программное обеспечение,), вы бы увидели дамп с указанием ошибки сегментации.

Это был не просто нас либо; Я говорил с людьми на других команд, все, кто был потоковое видео имел этот вопрос. Но, поскольку это случилось только раз в несколько минут, и матч это только 2:15, это не нужно работать очень долго, они просто скрестили пальцы и надеялись, что это не произойдет во время матча.

Приборная панель была написана в Java, и источник был доступен (под 3-п лицензии BSD), так что я нырнул в, охотясь за ошибки. Теперь, программа действительно использовал Java Native Interface, чтобы поговорить с OpenCV, который Видео пробежала; так что я подумал, что это должно быть ошибка в C / C ++ кода, который в настоящее время называется. Это было особенно боль, чтобы выследить указатели, которые вызывали проблемы, потому что это было трудно с носителями отладчиков видеть сквозь все JVM вещи к коду OpenCV, и OpenCV материал непрозрачным для Java отладчиков.

В конце концов вопрос привести меня обратно в Java код - было родное указатель хранится в переменной Java; Java кода, который называется родной процедуру для free() структуру, но затем попытался накормить его в другой процедуре позже. Это привело к затруднению снова - слежения за объектами с Java отладчиков было трудно, потому что они не ожидают программа вдруг сегментации; это Java-код, Java не сегментации, она бросает исключения!

С помощью println () я был в конечном счете в состоянии видеть, что некоторые код был исполняется в порядке, прямо не имеет смысла.

Ошибка

Вопрос в том, что Java делал небезопасный оптимизации (я никогда не удосужился выяснить, если это компилятор или JVM делает ошибку, я был доволен, когда я был обойти).

Java делает что-то подобное оптимизации хвост вызова, что касается сбора мусора. Вы смотрите, если он ждет возвращаемого значения метода m () объекта O , и код в m () , что до сих пор не выполняются не использовать другие методы или свойства О , тогда это будет идти вперед и рассмотреть о право для сбора мусора до m () закончит работу.

Это, как правило, безопасна оптимизация, чтобы сделать ... когда метод деструктор (за исключением finalize() ) определяется для объекта; Деструктор может иметь побочные эффекты, и Java не имеет возможности узнать, безопасно ли это для них произойдет до m () закончит работу.

Обойти

Процедура, которая ошибки сегментации происходило в что-то вроде:


public type1 getFrame() {
    type2 child = this.getChild();
    type3 var = this.something();
    // `this` may now be garbage collected
    return child.somethingElse(var); // segfault comes here
}

При использовании метода деструктор this требует способ, который будет free() родной памяти, также доступный от child ; если this является сборщиком мусора перед child.somethingElse () работает, резервного нативный код будет пытаться получить доступ к памяти, которая была free()  и получить ошибку сегментации. Это, как правило, не произошло, так как процедуры были довольно быстро. Тем не менее, работает 30 раз в секунду, в конце концов неудача с сборщиком мусора происходит, и сбое в работе программы.

Обойти было вставить фиктивный вызов этого, чтобы держать this  вокруг, пока после того как мы также были сделаны с child :


public type1 getFrame() {
    type2 child = this.getChild();
    type3 var = this.something();
    type1 ret = child.somethingElse(var);
    this.getSize(); // bogus call to keep `this` around
    return ret;
}

Да. Проведя недели пробираться через хотя тысячи строк Java, C, и C ++, фиктивный вызов метода я не заботился только о том, исправить.

Не нашли ответ на свой вопрос? Возможно, вы найдете решение проблемы на нашем канале в Youtube! Здесь мы собрали небольшие, но эффективные инструкции. Смотрите и подписывайтесь на наш youtube-канал!

Смотреть на Youtube