Содержание


Технология Java, стиль IBM

Классы общего пользования

Свойство разделения классов помогает уменьшить занимаемое место в памяти и улучшить начальный запуск приложения

Comments

Серия контента:

Этот контент является частью # из серии # статей: Технология Java, стиль IBM

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Технология Java, стиль IBM

Следите за выходом новых статей этой серии.

Принцип создания классов общего пользования между процессами виртуальной машины Java (JVM) уже не является новым. Например, свойство CDS, предоставленное фирмой Sun пишет системные классы в файл только для чтения, который имеет схему распределения памяти в JVM. В свойстве, называемое Shiraz, в IBM z/OS®1.4.2 JVM, использовала ведущую JVM, чтобы заполнить кэш классов, который затем был разделен подчиненными JVM.

Разработанная IBM версия 5.0 JVM основана на концепции, стоящей на ступеньку выше, которая позволяет всем классам системы и приложения храниться в постоянном динамическом кэше классов общего пользования. Данное свойство Разделенных Классов поддерживается на всех платформах, разработанных IBM, в которых есть JVM. Это свойство поддерживает даже внедренную модификацию среду выполнения байт-кодов, которая рассматривается в данной статье далее.

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

В добавление к поддержке классов общего пользования средой выполнения в JVM и поддержке ее загрузчиками классов, также предоставляется общедоступный интерфейс Помощника API для внедрения поддержки классов общего пользования в любые загрузчики классов, который рассматривается в данной статье подробнее.

Как оно работает

Давайте начнем с изучения технических деталей того, как работает свойство Разделения Классов.

Активация общего пользования классов

Вы активируете классы общего пользования с помощью добавления -Xshareclasses[:name=<cachename>] в существующую командную строку кода на языке Java. Когда запускается JVM, она ищет кэш классов, заданного имени (если имя не задано, она выбирает стандартное имя), и она либо подключается к существующему кэшу или создает новый, если это требуется.

Вы задаете размер кэша, используя параметр -Xscmx<size>[k|m|g]. Этот параметр применяется, только если JVM создала новый кэш. Если эта опция опущена, выбирается стандартное значение, в зависимости от платформы (обычно 16MБ). Заметим, что существуют настройки операционной системы, которые могут ограничивать количество разделенной памяти, выделенной на распределение -- например, SHMMAX в Linux обычно установлен в размере около 20MБ. Подробности этих настроек вы можете найти в главе о Разделенных Класса, соответствующего пользовательского справочника (см. главу Ресурсы для ссылки).

Кэш классов

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

Ни одна JVM не присваивает себе кэш, и не существует таких понятий, как ведущая или подчиненная JVM. Напротив, любое число виртуальных машин Java могут параллельно и читать, и писать в кэш. Кэш удаляет либо когда его явно уничтожают с помощью утилиты JVM, либо когда перезапускается операционная система (кэш не может продолжать работать после перезапуска операционной системы). Кэш не может расти в размерах и, когда он заполняется, JVM может все равно загружать классы из него, но не может добавлять другие классы. Существует набор утилит JVM для управления действующими кэшами, о котором более подробно говорится в главе, под названием "Утилиты разделяемых классов".

Как кэшируются классы?

Когда JVM загружает какой-либо класс, она, сперва, узнает, существует ли уже необходимый ей класс в кэше. Если он имеется в наличии, то виртуальная машина загружает его из кэша. Иначе, она загружает класс из файловой системы и записывает его в кэш в качестве части вызова defineClass(). Следовательно, неразделенная JVM имеет следующий порядок загрузки классов:

  1. Кэш загрузки классов
  2. Родительский
  3. Файловая система

Когда как в JVM, запускаемом с Разделением Классов, используется следующий порядок:

  1. Кэш загрузки классов
  2. Родительский
  3. Выделенный кэш
  4. Файловая система

Классы считываются и записываются в кэш с помощью общедоступного интерфейса Помощника API, который включен в разработанном IBM java.net.URLClassLoader. Следовательно, любой загрузчик классов, который расширяет java.net.URLClassLoader получает поддержку классов общего пользования бесплатно.

Какие части класса кэшируются?

Внутри виртуальной машины, разработанной IBM, классы Java делятся на две части: часть только для чтения, называемая ROMClass, которая содержит все постоянные данные класса, и часть RAMClass, которая содержит непостоянные данные, такие как статичные переменные класса. RAMClass указывает на данные в своем ROMClass, но обе эти части совершенно раздельны, что значит, что они достаточно безопасны для ROMClass, который разделять между виртуальными машинами Java и даже между классами RAMClasses в одной и той же JVM.

В случае, когда классы неразделены, JVM загружает класс, она создает ROMClass и RAMClass отдельно и хранит их в своей локальной памяти обработки. В случае, когда классы разделены, если JVM находит ROMClass в кэше класса, ей только нужно создать RAMClass в своей локальной памяти. Затем RAMClass адресует выделенный ROMClass.

По причине того, что большая часть данных класса хранится в ROMClass, это там, где происходит сохранение виртуальной памяти. (Глава "Отпечаток виртуальной памяти" рассматривает этот вопрос более подробно.) Время запуска JVM также ускоряется в значительной степени с заполненным кэшем, потому что часть работы по определению каждого кэшированного класса уже была произведена, и классы загружаются из памяти, а не из файловой системы. Издержки времени запуска для заполнения нового кэша (о котором я буду говорить далее в данной статье) не значительны, так как каждый класс необходимо переместить в кэш, как только он определен.

Что происходит, если класс изменяется в файловой системе?

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

JVM определяет обновления файловой системы тем, что она хранит значения меток времени в кэше и сравнивает кэшированные значения с действительными значениями при каждой загрузке классов. Если она определяет, что файл JAR был обновлен, она совершенно не знает, какие классы были изменены, поэтому все классы, загруженные из файла JAR, находящегося в кэше, моментально помечаются, как устаревшие и не могут больше быть скаченными из кэша. Когда загружаются классы из файла JAR данной файловой системы и снова добавляются в кэш, полностью добавляются лишь те классы, которые действительно были изменены. Другие же неизмененные классы фактически не помечаются, как устаревшие.

Классы стирать из кэша невозможно, но, тем не менее, JVM предпринимает попытки эффективно использовать имеющееся у нее пространство. Например, один и тот же класс никогда не добавляется дважды, даже если он загружается из очень разных источников. Таким образом, если один и тот же класс C3 загружается из /A.jar, /B.jar и /C.jar тремя разными виртуальными машинами Java, данные класса добавляется лишь один раз, но существует три кусочка метаданных, описывающих эти три источника, из которых класс был загружен.

Утилиты классов общего пользования

Существует набор утилит, которые можно использовать при управлении активными кэшами, каждый из которых является подопцией -Xshareclasses. (Вы можете получить полный список всех действительных подопций -Xshareclasses, набрав java -Xshareclasses:help.)

Заметим, что ни одна из утилит (за исключением expire) на самом деле не запускает JVM, и поэтому они производят запрашиваемое действие и уходят без запуска класса. Также отметим, что сообщение Could not create the Java virtual machine (не может создать JVM) выводится с помощью Java launcher каждой из утилит, потому что JVM не запущена. Это не является ошибкой.

Давайте разберем несколько примеров, чтобы продемонстрировать использование этих опций. Во-первых, давайте создадим два кэша, запустив класс HelloWorld с различными именами кэшей, как показано в Листинге 1:

Листинг 1. Создание двух кэшей
C:\j9vmwi3223\sdk\jre\bin>java -cp . -Xshareclasses:name=cache1 Hello
Hello
C:\j9vmwi3223\sdk\jre\bin>java -cp . -Xshareclasses:name=cache2 Hello
Hello

Запуск подопции listAllCaches выводит список всех кэшей в системе и определяет, используются они или нет, как вы можете увидеть в Листинге 2:

Листинг 2. Листинг всех кэшей
C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:listAllCaches
Shared Cache            Last detach time
cache1                  Sat Apr 15 18:47:46 2006
      
cache2                  Sat Apr 15 18:51:15 2006
      
Could not create the Java virtual machine.

Запуск опции printStats представляет вывод статистики по названному кэшу, как показано в Листинге 3. Для более подробного описания того, что означают все показанные поля, обратитесь к пользовательскому справочнику (см. Ресурсы для ссылки).

Листинг 3. Суммарная статистика для кэша
C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:name=cache1,printStats
      
Current statistics for cache "cache1":
      
base address       = 0x41D10058
end address        = 0x42D0FFF8
allocation pointer = 0x41E3B948
      
cache size         = 16777128
free bytes         = 15536080
ROMClass bytes     = 1226992
Metadata bytes     = 14056
Metadata % used    = 1%
      
# ROMClasses       = 313
# Classpaths       = 2
# URLs             = 0
# Tokens           = 0
# Stale classes    = 0
% Stale classes    = 0%
      
Cache is 7% full
      
Could not create the Java virtual machine.

Запуск опции printAllStats в названном кэше выводит действительное содержание кэша, наряду с выводом информации printStats. Каждый класс, хранящийся в кэше, находится в списке наряду с контекстные данные такие, как данные пути класса. В Листинге 4 вы можете увидеть список раскрутки пути класса виртуальной машины Java, за которым следует некоторые классы и подробная информация о том, откуда они были загружены:

Листинг 4. Листинг полного содержимого кэша
C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:name=cache1,printAllStats
      
Current statistics for cache "cache1":
      
1: 0x42D0FAB0 CLASSPATH
        C:\j9vmwi3223\sdk\jre\lib\vm.jar
        C:\j9vmwi3223\sdk\jre\lib\core.jar
        C:\j9vmwi3223\sdk\jre\lib\charsets.jar
        C:\j9vmwi3223\sdk\jre\lib\graphics.jar
        C:\j9vmwi3223\sdk\jre\lib\security.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmpkcs.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmorb.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmcfw.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmorbapi.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmjcefw.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmjgssprovider.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmjsseprovider2.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmjaaslm.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmjaasactivelm.jar
        C:\j9vmwi3223\sdk\jre\lib\ibmcertpathprovider.jar
        C:\j9vmwi3223\sdk\jre\lib\server.jar
        C:\j9vmwi3223\sdk\jre\lib\xml.jar
1: 0x42D0FA78 ROMCLASS: java/lang/Object at 0x41D10058.
        Index 0 in classpath 0x42D0FAB0
1: 0x42D0FA50 ROMCLASS: java/lang/J9VMInternals at 0x41D106E0.
        Index 0 in classpath 0x42D0FAB0
1: 0x42D0FA28 ROMCLASS: java/lang/Class at 0x41D120A8.
        Index 0 in classpath 0x42D0FAB0
    ...

Названные кэши можно уничтожить при помощи опции destroy, как показано в Листинге 5. Подобным образом, опция destroyAll уничтожает все кэши, которые не используются и у которых есть разрешение на то, чтобы пользователь мог их уничтожить.

Листинг 5. Уничтожение кэша
    C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:name=cache1,destroy
    JVMSHRC010I Shared Cache "cache1" is destroyed
    Could not create the Java virtual machine.
      
    C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:listAllCaches
    Shared Cache            Last detach time
    cache2                  Sat Apr 15 18:51:15 2006
      
    Could not create the Java virtual machine.

Опция expire, показанная в Листинге 6, являются управляющей опцией, которую вы можете добавить в командную строку, чтобы автоматически уничтожить кэши, к которым не было ничего привязано в качестве указанного числа минут. Это одна единственная утилита, которая не становиться причиной выхода JVM. Листинг 6 представляет кэши, которые не использовались в течение недели и теперь их уничтожают перед запуском виртуальной машины:

Листинг 6. Уничтожение КЭШей, которые не были использованы в течение недели
                C:\j9vmwi3223\sdk\jre\bin>java -cp .
                 -Xshareclasses:expire=10000,name=cache1 Hello
Hello

Вербализированные опции

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

Опция verbose, показанная в Листинге 7, предоставляет краткую информацию о статусе процессов запуска и выключения JVM:

Листинг 7. Получение информации о статусе JVM
C:\j9vmwi3223\sdk\jre\bin>java -cp .
 -Xshareclasses:name=cache1,verbose Hello
[-Xshareclasses verbose output enabled]
JVMSHRC158I Successfully created shared class cache "cache1"
JVMSHRC166I Attached to cache "cache1",
 size=16777176 bytes
Hello
JVMSHRC168I Total shared class bytes read=0. 
Total bytes stored=1176392

Опция verboseIO выводит строку статуса для каждого запроса на загрузку класса в кэш. Чтобы понять то, что выводит опция verboseIO, вам необходимо понять иерархию загрузчика классов. Это можно ясно увидеть в классах, которые загружаются любой программой загрузки без раскрутки. Каждый загрузчик классов должен передавать полномочия на иерархию загрузчику раскрутки, чтобы тот мог найти класс. При выводе информации, каждому загрузчику классов назначается уникальный номер ID, когда как загрузчик раскрутки всегда имеет номер 0.

Заметим, что это в порядке вещей для verboseIO иногда показывать классы, которые были загружены с диска и сохранены в кэше, даже если они уже были сохранены в кэше. Например, первая загрузка классов из каждого файла JAR пути классов приложения всегда происходит с диска и храниться вне зависимости от того, существует ли он в кэше или нет.

В Листинге 8 первая часть показывает состояние занятости кэша, а вторая часть показывает процесс чтения классов, сохраненных в кэше:

Подопция verboseHelper, представленная в Листинге 9 является улучшенной опцией, которая выдает информацию о статусе из интерфейса Помощника API. Она была создана для того, чтобы помочь разработчикам, использующим интерфейс Помощника API, понять, каким образом он запускается. Более подробно о данном выводе информации вы можете прочитать в диагностическом справочнике JVM (см. Ресурсы для ссылки).

Модификация среды выполнения байт-кода

Модификация среды выполнения байт-кода становится все более популярным средством реализации поведений в Java-классах. Ее можно выполнить при помощи средств интерфейса инструментов JVM Tools Interface (JVMTI) (см. Ресурсы для ссылки). Иначе, перед тем, как класс будет определен, байты классов могут быть перемещены загрузчиком классов. Это представляет собой большую трудность в классах общего пользования, так как одна JVM может кэшировать инструментированный байт-код, который не должен быть загружен другой JVM разделяя один и тот же кэш.

Однако, по причине динамической сущности реализации Разделенных Классов IBM, комплексные виртуальные машины, использующие различные типы модификации могут безопасно разделять один и тот же кэш. Более того, если модификация байт-кода ресурсоемка, кэширование модифицированных классов даже гораздо выигрышней, так как трансформацию необходимо выполнить только лишь один раз. Единственное условие в том, что модификации байт-кода должны быть предсказуемыми, и их можно было определить. После того, как класс был модифицирован и сохранен в кэш, его больше нельзя изменять.

Модифицированный байт-код можно разделять при помощи подопции modified=<context> опции -Xshareclasses. Контекст является именем, описанным пользователем, создающее сегмент в кэше, в котором хранятся все загруженные JVM классы. Все виртуальные машины, использующие данную модификацию, должны использовать соответствующее контекстное имя модификации. Также все они загружают классы из одного и того же сегмента. Любая JVM, использующая тот же кэш без подопции modified находит и хранит в себе унифицированные классы, как нормальные.

Потенциальные опасности

Если JVM запускается с агентом JVMTI, который зарегистрировал команду модифицировать байты классов и подопция modifiedне используется, то разделение классов с другими унифицированными виртуальными машинами или теми, которые используют других агентов, происходит все еще безопасно, тем не менее, с малым качеством выполнения из-за дополнительных проверок. Таким образом, рациональнее всего использовать подопцию modified.

Заметим, это возможно только по причине того, что JVM знает, что модификация байт-кода неизбежна вследствие существования агента JVMTI. Поэтому, если пользовательский загрузчик классов модифицирует байты классов перед тем, как определить класс без использования JVMTI и без использования подопции modified, определяемые классы распознаются как унифицированные и вследствие этого могут быть неверно загружены другими виртуальными машинами.

За более подробной информации о разделении модифицированного байт-кода обращайтесь в диагностический справочник JVM (см. Ресурсы).

Использование интерфейса Помощника API

Интерфейс Помощника API Разделенных Классов предоставленный IBM для того, чтобы разработчики могли интегрировать поддержку классов общего пользования в пользовательские загрузчики классов. Это требование только для загрузчиков классов, которые не расширяют java.net.URLClassLoader, по сравнению с загрузчиками автоматически наследующие поддержку классов общего пользования.

Данная статья не является исчерпывающим учебником по интерфейсу Помощника API, так как в ней лишь общий обзор темы. Если же вам необходимо подробное описание, полная документация по Java доступна в разделе Download, и также вы найдете дополнительную информацию в диагностическом справочнике (см. Ресурсы).

Интерфейс Помощника API: Краткое изложение

Все классы интерфейса Помщника API находятся в пакете com.ibm.oti.shared и содержаться внутри vm.jar в директории jre/lib. Каждый из загрузчиков классов, который будет разделять классы должен получить объект SharedClassHelper из SharedClassHelperFactory. Созданный вами SharedClassHelper принадлежит к загрузчику классов, который запрашивает его и может только хранить классы, определенные этим загрузчиком классов. SharedClassHelper дает загрузчику классов простой интерфейс API для поиска и хранения классов в кэше классов, к которому подключена JVM. Если в загрузчике классов производиться утилизация памяти, то относящийся к нему SharedClassHelper также проходит процесс GC.

Использование SharedClassHelperFactory

SharedClassHelperFactory представляет собой одноэлементное множество, которое получается посредством использования статического метода com.ibm.oti.shared.Shared.getSharedClassHelperFactory(), который возвращает factory, если в JVM активирована поддержка классов. Иначе, он возвращает null.

Использование SharedClassHelpers

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

  • SharedClassURLClasspathHelper: Этот помощник создан для использования загрузчиками классов, в которых заложен концепт URL-пути класса. Классы хранятся и располагаются в кэше при помощи массива URL-пути классов. Источники URL путей классов должны быть доступны в файловой системе для классов, чтобы их можно было сохранить в кэше. Этот помощник имеет некоторые ограничения на то, как может быть модифицирован путь класса в течение действия данного помощника.
  • SharedClassURLHelper: Этот помощник создан для использования загрузчиками классов, не имеют заложенный концепт пути класса и могут загружать класс с любого адреса URL. Данные источники URL должны быть доступны в файловой системе для классов, чтобы их можно было сохранить в кэше.
  • SharedClassTokenHelper: Этот помощник фактически превращает кэш классов общего пользования в простую таблицу устаревших данных (hashtable) -- классы хранятся напротив строковых ключевых лексем, которые не понятны для кэша. Это единственный помощник, который не предоставляет возможности динамического обновления, потому что сохраненные данные не имеют соответственного контекста файловой системы.

Каждый из SharedClassHelper имеет два базовых метода, параметры которых немного различаются в типах помощников:

  • byte[] findSharedClass(String classname...) должен быть вызван после того, как загрузчик классов запросил класс у родителя (если таковой существует). Если findSharedClass() не возращает null, то загрузчик классов должен вызвать defineClass() в возвращаемый байтвый массив. Отметим, что эта функция возвращает специальный файл cookie для defineClass(), недействительные байты класса так, что байты невозможно больше реализовывать.
  • boolean storeSharedClass(Class clazz...) должны быть вызваны немедленно после определения класса. Метод возвращает true, если данный класс был успешно сохранен, в противном случае он возвращает false.

Другие случаи

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

Безопасность

В стандартной настройке, кэши классов создаются с пользовательским уровнем безопасности, таким образом, только пользователь, создавший этот класс, имеет к нему доступ. По этой причине, стандартное имя кэша отличается для каждого пользователя, и расхождений в этом плане нет. В UNIX существует подопция для настройки groupAccess, которая предоставляет доступ всем пользователям в исходной группе пользователей, создавших данный кэш. Однако, несмотря на используемый уровень доступа, кэш может быть уничтожен только пользователем, создавшим его, или администратором.

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

Утилизация памяти и синхронное компилирование

Работа с классами общего пользования никак не влияет на процесс утилизации памяти (GC). Классы и загрузчики классов также проходят утилизацию памяти, как и в случае классов общего пользования. Также при активированных классах общего пользования у методов GC нет никаких ограничений или конфигураций.

В кэше класса невозможно кэшировать синхронно (JIT) скомпилированный код, поэтому при активированном разделении классов в синхронности нет никаких изменений.

Пределы размеров кэша

Текущий теоретический максимальный размер кэша равен 2 ГБ. Размер кэша ограничен следующими факторами:

  • Доступное пространство на диске (только для Microsoft Windows). Файл распределения памяти создается в директории под названием javasharedresources для хранения данных классов. Эта директория создается в пользовательской директории %APPDATA%. Файлы классов общего пользования уничтожаются каждый раз, когда вы перезапускаете систему Windows.
  • Доступное пространство в системе (только для UNIX). В UNIX кэш находится в разделенной памяти и файл конфигурации записывается с помощью JVM в /tmp/javasharedresouces, чтобы дать доступ всем виртуальным машинам распределять разделенные области памяти поимененно.
  • Доступное виртуальное адресное пространство. По причине того, что виртуальное адресное пространство процессора разделяется между кэшем классов общего пользования и динамической памятью Java, увеличение максимального размера динамической памяти Java уменьшает размер кэша классов общего пользования, который вы хотите создать.

Пример

Чтобы продемонстрировать вам преимущества разделения классов на практике, в этом разделе представлено простое демо (demo) в графике. Исходные данные и двоичные коды доступны в разделе Download.

Демо-приложение ищет директорию jre\lib и открывает все файлы JAR, под названием class.forName(), в каждом из найденных классов. Вследствие этого примерно 12000 классов должны быть загружены виртуальной машиной. Демо выдает отчеты о том, как много времени требуется JVM, чтобы загрузить классы. Очевидно, что это довольно надуманный пример, так как в нем производится лишь загрузка классов. Зато данный пример отлично демонстрирует преимущества классов общего пользования. Давайте запустим приложением и посмотрим на результаты.

Качество загрузки классов

  1. Скачайте shcdemo.jar из раздела Download.
  2. Запустите тест пару раз без разделения классов для разогрева кэша системного диска при помощи команды, приведенной в Листинге 10:

    Листинг 10. Разогрев кэша системного диска
       C:\j9vmwi3223\sdk\jre\bin>java -cp C:\
       shcdemo.jar ClassLoadStress


    Когда появится такое окно, как на Рисунке 1, нажмите кнопку. И приложение загрузит классы.

    Рисунок 1. Нажмите кнопку
    Нажмите кнопку
    Нажмите кнопку


    Как только загрузка классов завершилась, приложение выдаст отчет о том, сколько классов оно загрузило и сколько времени для этого потребовалось, как показано на Рисунке 2:

    Рисунок 2. Вот и результаты!
    Вот и результаты
    Вот и результаты


    Вы заметите, что приложение, вероятно, немного ускоряется при каждом новом запуске. Это происходит благодаря оптимизации операционной системы.
  3. Теперь запустите демо с активированным разделением классов, как показано в Листинге 11. Создается новый кэш, и данный запуск приложения показывает, сколько времени требуется для заполнения нового кэша. Вам необходимо задать размер кэша, около 50MБ, чтобы удостовериться, что места достаточно для всех классов. Листинг 11 показывает командную строку и пример вывода.

    Как изображено на Рисунке 3, этот запуск потребует немного больше времени, чем предыдущие попытки, так как демо заполняет кэш классов общего пользования. Вы также на свое усмотрение можете использовать команду printStats, как показано в Листинге 12 для того, чтобы посмотреть число хранящихся классов в кэше классов общего пользования:

    Рисунок 3. Результаты не разогретого кэша
    Результаты не разогретого кэша
    Результаты не разогретого кэша
    Листинг 12. Просмотр нескольких кэшированных классов
    C:\j9vmwi3223\sdk\jre\bin>java -Xshareclasses:name=demo,printStats
            
    Current statistics for cache "demo":
            
            
    base address       = 0x41D10058
    end address        = 0x44F0FFF8
    allocation pointer = 0x44884030
            
    cache size         = 52428712
    free bytes         = 6373120
    ROMClass bytes     = 45563864
    Metadata bytes     = 491728
    Metadata % used    = 1%
            
    # ROMClasses       = 12212
    # Classpaths       = 3
    # URLs             = 0
    # Tokens           = 0
    # Stale classes    = 0
    % Stale classes    = 0%
            
    Cache is 87% full
           
    Could not create the Java virtual machine.
  4. Теперь запустите демо снова при помощи той же самой командной строки Java. На этот раз оно должно считывать классы из кэша классов общего пользования, как можно увидеть в Листинге 13.

    Вы можете четко увидеть значительное улучшение во времени загрузки классов. Теперь вы обязательно увидите улучшение качества работы почти при каждом запуске демо благодаря оптимизациям операционной системы. Данный тест проходил на однопроцессорном 1.6 GHz x86-совместимом лэптопе с Windows XP:

    Рисунок 4. Результаты разогретого кэша
    Результаты разогретого кэша
    Результаты разогретого кэша

Вы можете экспериментировать разными способами. Например, вы можете использовать команду javaw и запустить несколько демо и на всех вместе запускать загрузку классов, чтобы увидеть качество параллельной работы приложений.

В действительности же общее преимущество времени запуска JVM при использовании классов общего пользования зависит от числа классов, загружаемых приложением: программа HelloWorld не сможет показать заметного преимущества, когда как большой веб-сервер действительно покажет. Однако, данный пример, наверняка, смог продемонстрировать, что такое экспериментирование с разделением классов очень эффективно, и вам можно легко доступно возможность тестирования тех или иных преимуществ.

Пространство виртуальной памяти

При запуске примера программы с более одной JVM, вам также легко увидеть сохранения виртуальной памяти.

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

Рисунок 5. Пять демо без разделения классов
Пять демо без разделения классов
Пять демо без разделения классов
Рисунок 6. Пять демо с разделением классов
Пять демо с разделением классов
Пять демо с разделением классов

Теперь вам ясно видно, что затраты ресурсов с активированным разделением классов значительно ниже. ОС Windows пытается подсчитать затраты ресурсов, складывая размеры VM вместе. По причине того, что количество разделенных данных классов в кэше равно около 45MБ, вы видите, что использование памяти на каждую JVM равно примерно размеру VM плюс количество данных классов в кэше.

В данных двух примерах начальные затраты ресурсов были равны около 295MБ. Это означает, что в первом примере использовалось 422MБ, когда как во втором примере использовалось 244MБ -- разница в 178MБ.

Заключение

Новое свойство Разделения Классов в реализации платформы Java версии 5.0, разработанной IBM предоставляет простой и удобный путь уменьшения места, занимаемого виртуальной памятью и улучшить время запуска JVM. В данной статье, вы изучили, как активировать это свойство, как использовать утилиты кэша и как получать качественные измерения преимуществ.

Следующая статья данного выпуска представит вам некоторые новые инструменты отладки, мониторинга и профилирования, которые доступны на платформе Java, разработанной IBM. Также в ней мы расскажем о том, как быстро их можно использовать при профилировании и отладке Java-приложений.


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=165720
ArticleTitle=Технология Java, стиль IBM: Классы общего пользования
publish-date=12182006