49786 (597463), страница 19
Текст из файла (страница 19)
Фаза маркировки. Сборщик мусора просматривает области локальных переменных всех активных в настоящий момент методов, а также поля всех доступных объектов. В дескрипторах тех объектов, на которые есть ссылки в просмотренных областях "признак мусора" сбрасывается
Фаза очистки. Просматривается область кучи, дескрипторы всех объектов. Те объекты, "признак мусора" которых оказывается взведенным (не был сброшен в фазе маркировки), являются мусором, занимаемая ими память освобождается. У тех же объектов, "признак мусора" которых сброшен, этот признак взводится - для подготовки к следующей сборке мусора.
Затраты на выполнение сборки мусора практически не зависят от количества мусора - в любом случае требуется полный просмотр и областей локальных переменных, и кучи. Следовательно, сборку мусора выгоднее производить только в те моменты, когда мусора накопится много: в этом случает при тех же затратах будет получен больший результат. Поэтому операция сборки мусора может создавать некоторую проблему при выполнении Java-программ. Проблема состоит в том, что момент активизации сборщика мусора непредказуем, а когда такая активизация произойдет, она вызовет задержку в вычислениях. В новых реализациях Java VM эту проблему стараются если не решить кардинально, то несколько сгладить, запуская сборщик мусора в отдельной низкоприоритетной нити.
Хотя область методов тоже формально принадлежит куче, в большинстве современных Java VM сборщик мусора в этой области не работает.
13.5 Защита ресурсов
Поскольку одной из основных сфер применения технологии Java является Internet, вопросы безопасности для этой технологии приобретают особое значение. Безопасность в сетевой среде представляет собой целый комплекс сложных вопросов, рассматриваемых в отдельном курсе. Здесь же мы уделим основное внимание защите локальных ресурсов - анализу возможности Java-программы получить несанкционированный доступ к ресурсам на том компьютере, на котором она выполняется.
Прежде всего, в самих языковых средствах Java отсутствуют некоторые возможности языка C/C++, которые наиболее часто приводят к неправильному использованию ресурсов - случайному или намеренному. Главная черта языка Java в этом отношении - отсутствие указателей. Хотя доступ к объектам в Java осуществляется по ссылкам, и физический смысл ссылки и указателя C/C++ одинаков - адрес памяти, ссылка не есть указатель. Различие состоит в том, что, во-первых, ссылка не может быть преобразована в число или какое-либо иное представление физического адреса, во-вторых, над ссылками недопустимы арифметические операции. Именно адресная арифметика в C/C++ является средством, использование которого может привести к доступу процесса за пределы той области памяти, к которой он имеет право обращаться.
Другой "лазейкой" для выполнения несанкционированных действий в языке C/C++ является слабая защита типов. C/C++ позволяют использовать типы данных в операциях, этому типу не свойственных - путем неявного преобразования типов или путем приравнивания разнотипных указателей (в том числе, и для интегрированных типов). В Java осуществляется строгий контроль типов и в большинстве случаев требуется явное преобразование типов.
Автоматическое освобождение памяти в Java также является свойством, повышающим защищенность. Можно говорить также и о том, что более последовательное воплощение в Java парадигмы объектно-ориентированного программирования также является выигрышным обстоятельством с точки зрения защиты.
Следует оговорить, что указанные различия между языками C/C++ и Java обусловлены прежде всего тем, что языки ориентированы на разные сферы применения. Те "недостатки" языка C/C++, на которые мы указываем, превращаются в уникальные достоинства при применении С/С++ в качестве языка системного программирования, а именно таково первоначальное предназначение этого языка. При разработке же приложений (а Java - язык именно для разработки приложений) эти возможности становятся ненужными и даже опасными.
Однако сами свойства языка Java еще не являются гарантией защищенности. Они обеспечиваются компилятором Java, но не предохраняют от модификации исполняемый модуль. Поскольку спецификации байт-кода Java и файла класса открыты, программы, осуществляющие несанкционированный доступ, могут писаться непосредственно в байт-кодах или на других языках с компиляцией в байт-код Java. Чтобы перекрыть этот канал несанкционированного доступа, в платформе Java выполняется верификация байт-кода.
Процесс верификации состоит из четырех шагов.
Шаг 1 выполняется при загрузке класса. При этом Java VM проверяет базовый формат файла класса - "магическое число" и номер версии, соответствие размера файла суммарному размеру его составляющих, формальное соответствие отдельных структур спецификациям.
Шаг2 выполняется при связывании, он включает в себя верификацию без анализа байт-кодов. На этом шаге проверяется:
отсутствие нарушений в использовании классов и методов, объявленных с модификатором final;
наличие у каждого класса (кроме класса Object) суперкласса;
соответствие спецификациям содержимого пула констант;
правильность имен классов и интерфейсов и дескрипторов всех полей и методов, ссылающихся на пул констант.
Проверки правильности элементов файла класса, выполняемые на этом шаге, - только формальные, не семантические. Более подробные проверки выполняются на следующих шагах.
Шаг 3 также выполняется на этапе связывания. На этом шаге верификатор проверяет массив байт-кодов каждого метода. При этом анализируется поток данных, обрабатывающийся при выполнении метода. Верификатор исходит из того, что в любой точке программы, независимо от того, каким образом управление попало на эту точку, должны соблюдаться определенные ограничения целостности данных, которые сводятся в основном к следующим:
размер стека операндов неизменен и стек содержит операнды одного типа;
не выполняется доступ к локальным переменным неизвестного типа;
доступ к локальным переменным осуществляется только в пределах массива локальных переменных;
все обращения к пулу констант производятся к элементам соответствующего типа;
полям класса назначаются значения соответствующего типа;
все команды байт-кода используются с операндами (в стеке или в массиве локальных переменных) типа, соответствующего типу команды;
методы вызываются с правильными аргументами;
команды перехода передают управление только внутри байт-кода метода и передача управления всегда происходит только на первый байт команды байт-кода.
Шаг 4 выполняется при первом вызове кода любого метода. Это "виртуальный шаг", он выполняется не в виде отдельного шага проверки всего байт-кода, а при выполнении каждой отдельной команды.
Для команды, которая ссылается на тип, при этом:
загружается определение типа (если оно еще не загружено);
проверяется, может ли текущий выполняемый метод ссылаться на этот тип;
выполняется инициализация класса (если он еще не инициализирован).
Для команды, которая вызывает метод или осуществляет доступ к полю класса, при этом:
проверяется, существует ли поле или метод в данном классе;
проверяется правильность дескриптора вызванного метода или поля;
проверяется, имеет ли текущий выполняемый метод права доступа к этому методу или полю.
В конкретных реализациях Java VM допускается после выполнения шага 4 заменять проверенную команду байт-кода альтернативной "быстрой" формой. Например, в Sun Java VM команда байт-кода new может быть заменена командой new_quick. "Быстрая" команда выполняется так же, как и исходная, но при ее выполнении исключается повторная верификация команды. В файле класса "быстрые" команды не допускаются, они выявляются на предыдущих шагах верификации и вызывают отказ. "Быстрые" формы не являются спецификациями Java, они реализуются в конкретной Java VM.
Аплеты являются наиболее критическими с точки зрения безопасности Java-программами, поскольку аплет загружается из Internet, возможно, из непроверенного источника. Естественно, недопустимым является предоставление программе, пришедшей "неизвестно откуда" доступа к ресурсам локального компьютера. Поэтому для аплетов введены весьма жесткие ограничения на выполнение. Аплету запрещается:
получать сведения о пользователе или его домашней директории;
определять свои системные переменные;
работать с файлами и директориями на локальном компьютере (читать, изменять, создавать и т.д. и даже проверять существование и параметры файла);
осуществлять доступ по сети к удаленному компьютеру, получать список сетевых сеансов связи, которые устанавливает локальный компьютер с другими компьютерами;
открывать без уведомления новые окна, запускать локальные программы и загружать локальные библиотеки, создавать новые нити, получать доступ к группам нитей другого аплета;
получать доступ к любому нестандартному пакету, определять классы, входящие в локальный пакет.
Модель безопасности Java еще далека от совершенства, и в ее реализациях иногда обнаруживаются "лазейки" для несанкционированного проникновения. Следует отметить, что в сетевых публикациях довольно часто можно встретить критику безопасности в Java и предупреждение о принципиальной возможности "взлома" защиты Java тем или иным способам. Вместе с тем, сетевые публикации не дают оснований говорить о том, что реальные информационные системы, в которых применяется технология Java, чаще подвергаются взлому, чем системы, эту технологию не применяющие.
13.6 JavaOS и Java для тонких клиентов
В конце 90-х годов фирма Sun Microsystems предприняла разработку новой ОС, базирующейся на технологии Java - JavaOS. Для доводки этой ОС фирма Sun привлекла фирму IBM, и конечный продукт JavaOS является собственностью обеих этих фирм.
JavaOS является операционной системой для широкого спектра вычислительных средств, включая сетевые и встроенные компьютеры. Целью разработки этой ОС являлось предоставление среды для выполнения Java-приложений без использования базовой универсальной ОС.
JavaOS строится по принципу многослойной архитектуры, показанной на рисунке 13.6, включающей в себя платформенно-зависимую и платформенно-не
Рисунок 13.6 Архитектура JavaOS
Платформенно-зависимая часть состоит из загрузчика, микроядра, виртуальной машины Java и частично - среды выполнения Java (JavaOS Runtime Environment).
Функции загрузчика вытекают из его названия. JavaOS ориентирована прежде всего на клиент/серверную модель вычислений. Это означает, что необходимое программное обеспечение, постоянно хранящееся на клиентской стороне - минимальное, загрузчик и составляет тот необходимый и достаточный минимум программного обеспечения, который обеспечивает загрузку всего остального программного обеспечения с сервера.
Микроядро JavaOS очень похоже на микроядра ОС, рассмотренных нами выше (QNX, AMX RTOS и др.). Оно выполняет функции:
обработки прерываний и исключений;
поддержки множественных нитей;
поддержки многопроцессорных конфигураций;
управления реальной памятью;
управления реальными устройствами и каналом ПДП.
JVM обеспечивает:
интерпретацию байт-кода;
управление выполнением;
управление памятью;
нити;
загрузку классов;
верификацию байт-кода.
Программное обеспечение среды выполнения Java частично создается в кодах Java, частично - в "родных" (native) кодах целевой платформы. В состав среды выполнения входит JVM и ряд системных менеджеров, в том числе:
Менеджер Конфигурации, представляющий собой первый класс, Java-кода, выполняемый JVM, он обеспечивает запуск компонентов Менеджера Платформы и дополнительных сервисов JavaOS;
Менеджер Платформы, обеспечивающий запуск и поддержку компонентов, обслуживающих платформенно-зависимые устройства и шину ввода-вывода платформы;
Менеджер Сервисов, пакет, обеспечивающий поиск и запуск сервисных утилит JavaOS;
Менеджер Устройств, компонент, обеспечивающий архитектуру Java-интерфейса устройств (JDI);
Классы Java-интерфейса платформы (JPI), инкапсулирующие драйверы JDI и решение платформенно-зависимых вопросов, включая управление временем, памятью и прерываниями.
Дополнительные (опционные) компоненты среды выполнения включают в себя компоненты конфигурации (персональной, сетевой, встроенной), наборы драйверов и средства отладки.
Вся среда выполнения (включая JVM) работает как один процесс в виртуальном адресном пространстве. Соответствие виртуального адресного пространства физической памяти обеспечивается микроядром. Также микроядро обеспечивает использование многопроцессорной архитектуры вычислительной системы для функционирования среды выполнения и приложений. Вся специфика управления процессорами и памятью инкапсулирована в JPI.
Большая часть драйверов устройств JavaOS пишется на языке Java. Платформенная независимость драйверов поддерживается компонентом JDI, который состоит из:
Менеджера Событий, обеспечивающего взаимодействие с устройствами по событийной модели;
Системной Базы Данных, обеспечивающей хранение и получение конфигурационной информации (относящейся к ОС, устройствам и приложениям) в едином репозитории;