Лекция
Привет, Вы узнаете о том , что такое двоичная совместимость, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое двоичная совместимость, бинарная совместимость , настоятельно рекомендую прочитать все из категории Разработка программного обеспечения и информационных систем.
Двои́чная совмести́мость, бина́рная совмести́мость (англ. binary compatibility) — вид программной совместимости, позволяющий программе работать в различных средах без изменения ее исполняемых файлов.
Этот термин часто используется в значении «совместимость операционных систем», и в таком случае означает способность уже скомпилированной версии программы для одной операционной системы работать в другой операционной системе без перекомпиляции. К примеру, практически все программы, написанные для Windows 2000, можно запустить в Windows XP — это означает, что Windows 2000 и Windows XP бинарно (двоично) совместимы.
двоичная совместимость включает в себя побайтовую совместимость полей загрузки, полную идентичность механизма вызова функций, передачи переменных и получения результата вычислений, и полную реализацию интерфейса программирования. При этом технически реализация может быть совершенно иной, — главное, чтобы были реализованы все вызовы и чтобы они приводили к ожидаемому результату, а каким способом этот результат достигается, решают создатели программы.
Слом двоичной совместимости означает прекращение поддержки программ и обязательное требование перекомпиляции и возможных исправлений в программе, чтобы она заработала. К примеру, после того, как компания Apple начала использовать в своих компьютерах процессоры Intel, была сломана двоичная совместимость со всеми приложениями, написанными для процессоров PowerPC. Чтобы не лишиться всех разработанных в прошлом программных продуктов для операционной системы Mac OS, компания Apple использует легкий транслятор Rosetta, переводящий вызовы операционной системы Mac OS для PowerPC в вызовы Mac OS для Intel. Этот пример демонстрирует также возможный метод борьбы со сломом двоичной совместимости.
В операционной системе Solaris 10 для x86 существует возможность запускать приложения для Linux без перекомпиляции с помощью BrandZ. На платформе SPARC такая возможность отсутствует.
Возможно, многие из вас задавались вопросами вроде «А что будет, если кто-то подложит к моему приложению неправильную версию библиотеки?». Вопрос хороший, а ответ на него и некоторые другие вы найдете в этом топике. Для затравки задачка: пусть есть два интерфейса и класс, реализующий один из них:
А также класс, в котором есть метод foo
, перегруженный для A
и B
. Этот метод вызывают от экземпляра класса C
:
Вполне очевидно, что выведется «A». Не менее очевидно, что если сказать, что C implements A, B
, то получится ошибка компиляции (тем, кому последнее не очевидно, могу порекомендовать почитать про то, как происходит выбор методов. Например, в стандарте в разделе 15.12.2 или в более просто описывающих местах).
Но вот что произойдет, если мы перекомпилируем только C.java
, а потом запустим CompatibilityChecker
из уже имеющегося класс-файла, является уже более сложным вопросом. Заинтересованы? Прошу под кат!
Те, кто знают, что перегруженные методы выбираются во время компиляции, могут сообразить, что по этой причине в класс-файле будет сразу записана информация о том, какой же метод вызывать, и потому в результате выведется «A». Проверим это предположение:
Действительно, как можно заметить по инструкции с отступом 19, идет вызов вполне конкретного метода. Впрочем, те, кто слышал про верификатор, могут возразить и предположить, что он заметит, что это какие-то не те пчелы класс C
изменился, и швырнет исключение. К счастью, они ошибаются, ведь верификатор проверяет только лишь корректность структуры классов и интерфейсов, а не соответствие версий класс-файлов.
Итак, запустим-таки наш код и убедимся, что изначальное предположение было правильным: выведется действительно «А».
Кроме того, можно предположить, что в виртуальной таблице адресов может оказаться какой-то неправильный адрес, и потому все сломается в рантайме с NoSuchMethodError
. Об этом говорит сайт https://intellect.icu . Это предположение тоже ошибочно, так как вызывается метод foo(A)
, и в виртуальной таблице он такой один. Другое дело, если бы были наследники, его переопределяющие…
Пусть у нас есть три следующих класса:
И класс, вызывающий foo
в разных вариантах:
Все, конечно же, знают, что поскольку у переотпределенных типов методы выбираются в рантайме, изначальный вывод будет таким:
Самое время сделать пакость, подменив класс-файл от A
на результат компиляции следующего кода:
Понять, что в данном случае произойдет, довольно просто. Во-первых, все попытки вызвать методы, где foo
вызывается у экземпляпа класса A
, точно вылетят с NoSuchMethodError
. Среди этих попыток также находится и вызов super.foo()
в классе C
. Во-вторых, как мы уже видели раньше, метод B.foo()
вызовется успешно.
Теперь изменим тактику: снова сделаем A.foo
таким, каким он и был, но теперь поменяем B
и C
, вообще удалив из них переопределение метода foo
:
При запуске кода dynamic dispatch обнаружит только по одной записи для A.foo
, и потому во всех случаех вызовет его, в результате чего мы увидем в консоли только буквы «A» и полное отсутствие каких-либо исключений.
Продолжим наши изыскания, снова переопределив методы в B
и C
. После запуска, как мы и можем ожидать, dynamic dispatch обнаружит все записи в виртуальной таблице, и даст точно такой же вывод, как и тот, который мы получили бы, заново перекомпилировав все.
Ранее мы пробовали экспериментировать только с методами. Посмотрим теперь, что бывает с полями. Пусть есть класс, хранящий значение типа int и наследник этого класса:
И, традиционно, потребитель класса B
:
Добавим теперь в класс B
собственное поле с таким же именем:
Посмотрим, какой байт-код был сгенерирован для CompatibilityChecker
:
Этот листинг может сбить с толку, полкольку на отступе 11 в комменте вроде как сказано, что поле принадлежит B. Потому стоит полагать, что при перекомпиляции B
мы столкнемся с ошибкой. Однако оказывается, что это вовсе не так. Поскольку физически поле есть только у базового класса, операнд команды putfield
указывает именно на то самое нужное поле, в результате чего после изменений код продолжает работать.
В спецификации под бинарную совместимость отведена целая глава, в которой основным понятием является «двоично совместимое» или «безопасное» изменение. В спецификации утверждается, что при внесении только безопасных изменений гарантируется безопасное выполнение приложения без перекомпиляции всего остального и ошибок линковки. Как ни странно, но во всей огромной главе нет точного определения двоично совместимой операции, однако есть куча примеров:
Возможно, именно из-за такого слабого определения двоично совместимых операций, возникают проблемы.
Как выяснилось, спецификация недостаточно строга, и можно придумать случай, в котором безопасные изменения приведут к ошибке в линковке. Рассмотрим интерфейс, класс, реализующий этот интерфейс, и еще один класс, использующий их:
Произведем два безопасных изменения: добавим метод foo
в интерфейс A
и изменим реализацию метода main
класса CompatibilityChecker
:
При запуске, как вы могли понять, произойдет ошика, а именно AbstractMethodError: B.foo()V
, чего по идее быть не должно. Эту проблема известна и лежит в самой основе обработки байт-кода Java. Были предложения по исправлению ситуации, но они пока ни к чему не привели.
Итак, ответ на вопрос, профигурировавший в самом начале статьи («А что будет, если кто-то подложит к моему приложению неправильную версию библиотеки?») такой: «А кто ж его знает. Смотря в чем та версия, которая использовалась при компиляции, отличается от той, которая используется в рантайме».
В статье не затронуты некоторые очевидные вещи. Например, что при несовместимых изменениях вроде удаления методов и классов или превращения класса в интерфейс, будут кидаться беспощадные ошибки вроде NoSuchMethodError
, NoClassDefFoundError
или IncompatibleClassChangeError
.
Исследование, описанное в статье про двоичная совместимость, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое двоичная совместимость, бинарная совместимость и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Разработка программного обеспечения и информационных систем
Из статьи мы узнали кратко, но содержательно про двоичная совместимость
Комментарии
Оставить комментарий
Разработка программного обеспечения и информационных систем
Термины: Разработка программного обеспечения и информационных систем