On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru






 

Переход к реализованной IBM 64-разрядной JVM для 64-разрядного издания Windows 2000

Появление следующего поколения процессоров Intel 64-разрядного Itanium и 64-разрядных операционных систем занчительно увеличивает способности и производительнось, доступные прикладным программам. IBM работала вместе с Intel, чтобы сделать Виртуальную Машину Ява (JVM) доступной как можно раньше для разработчиков 64-разрядных приложений в этой новой среде. В то время как 100% чистые Java приложения исполняются в новой среде, не требуя модификации, Java приложения с native кодом требуют небольшого количества усилий для их перенесения. Эта статья обсуждает причины для переноса приложений Java в 64-разрядную среду и сам процесс перенесения native кода Java из 32-разрядной Windows в 64-разрядную среду Windows.

Должны ли вы переходить к 64-разрядной среде?

Первая причина для переноса приложений из 32-разрядной в 64-разрядную среду состоит в том, чтобы предоставить приложениям доступ к очень большому (свыше 4 Гб) объёму памяти. Хотя множество маленьких приложений не получат никакой выгоды от этого, те приложения, которые могут эффективно использовать большие объемы памяти повысят свою производительность. Клиентские приложения трехмерной графики, требующие интенсивных вычислений и обращений к памяти, серверные приложения, такие как базы данных, и серверы электронного бизнеса уже осуществляют переход, чтобы использовать увеличенные объемы памяти, становящиеся доступными в 64-разрядной среде.

Подобно любому другому приложению, перенесенному в 64-разрядную среду Windows 2000, среда, которая включает реализованную IBM JVM имеет доступ к очень большим объемам памяти. Что позволяет среде, включающей JVM, создавать очень большие "кучи" (возможно до нескольких гигабайт в размере) и управлять ими. Это в свою очередь дает возможность улучшить производительность тех приложений Java, которые могут извлечь выгоду из использования очень больших объемов динамической памяти.

Много маленьких приложений на C, которые требуют умеренных объемов памяти, не смогут получить выигрыш от перенесения в 64-разрядную среду из-за "раздутия кода"("code bloat") 64-разрядного кода по сравнению с более компактным форматом 32-разрядного кода. Точно так же, хотя 100% "чистые" приложения Java не требуют никакого перенесения, выбор конкретной используемой JVM может зависеть от характера приложения Java. Если приложение Java использует умеренные объемы динамической памяти и её устраивает существующий подход к сборке "мусора", то для неё может быть лучше использовать существующую среду, применяющую реализованную IBM 32-разрядную JVM, которая исполняется под 64-разрядной Windows 2000. Приложения Java, включающие native код, использующий Java Native интерфейс (JNI), которые должны быть перенесены в 64-разрядную среду по другим причинам, должны использовать среду с 64-разрядной JVM. Они больше не могут использовать среду с 32-разрядной JVM, потому что 64-разрядная операционная система Windows не разрешает 32-разрядным и 64-разрядным приложениям сосуществовать в пределах того же самого процесса.

Figure 1

Рисунок 1. Возможный выбор JVM и доступные JVM для 64-разрядного Windows 2000 на IA-64

На рисунке показаны возможные среды JVM, которые могут быть доступны для 64-разрядной Windows 2000 и те из них, которые сделаны IBM. Существующая реализованная IBM 32-разрядная JVM сформирована для архитектуры набора команд (ISA) IA-32 (архитектура Intel) и в настоящее время доступна в 32-разрядных Windows 95, 98, NT, и 2000. Также она исполняется под 64-разрядной Windows 2000 и может продолжать использовать существующий 32-разрядный IA-32 ISA native код, не требуя его изменения. Новая реализованная IBM 64-разрядная JVM сформирована для IA-64 ISA и будет работать с 64-разрядным native кодом сформированным для IA-64 ISA. IBM сделала недоступной 32-разрядную JVM сформированную для IA-64 ISA, так что не будет возможна работа 32-разрядного native кода, сформированного для IA-64 ISA, с реализованной IBM JVM.

Изменяющаяся программная модель

32-разрядная Windows поддерживает модель данных ILP32, в которой целые и вещественные числа, и указатели имеют длину в 32 бита. 64-разрядная Windows поддерживает новую модель данных P64, в которой указатели имеют длину 64 бита, а integer и long остаются 32-разрядными. При переходе в 64-разрядную среду нужно будет преодолеть очевидные проблемы кода на С. Сюда включаются типы неназначенных указателей на long и integer и обеспечение того, чтобы шаблоны форматного printf применяли символы правильной замены для указателей. Рассмотрим целочисленные типы. 32-разрядное целое число может быть достаточно большим, чтобы содержать все возможные значения в 32-разрядной среде, такие как количество свободной памяти в буферном пуле. Однако, то же самое 32-разрядное целое число неспособно содержать верное значение в 64-разрядной среде, где максимальное значение может быть во много раз большее, чем максимальное 32-разрядное целое число. Для этого потребуется 64-разрядное целое число.

Полное обсуждение изменений среды C и рассмотрение путей создания 32/64-разрядного нейтрального native кода доступно в различных источниках. Обращайтесь по ссылкам, указанным в конце этой статьи. JNI, который будет использоваться в 64-разрядной среде, отражает эти и другие изменения в его определениях.

Значения (Implications) для JNI

Чтобы учесть будущие потребности, определение jsize изменилось. Jsize в 32-разрядной среде остается того же самого размера как jint (32 бита) , но в 64-разрядной среде его размер - jlong (64 бита) . При компиляции в 64-разрядной среде правильно написанного 32-разрядного native кода можно не обращать внимание на это изменение. Однако, проблемы могут происходить с native кодом, в котором переменной типа jint присваивается значение jsize.

Увеличение размера native указателя (или дескриптора) с 32 до 64 разрядов в 64-разрядных средах становится более проблематичным, если native код хранит эти значения внутри Java объектов. Потому что пространство, требуемое, чтобы сохранить значение в пределах Java объекта, должно быть соответствующее увеличено в размере. Как лучше решить эту проблему, зависит от потребностей приложения Java.

Если приложение Java, которое хранит native указатели в Java объектах, переносится в 64-разрядную среду, и оно больше не будет поддерживаться в 32-разрядной среде, то самое простое решение состоит в том, чтобы изменить тип с int на long для тех переменных, которые используются для хранения значения native указателя. После замены типа полей, хранящих значения native указателя, native код, который читает и записывает значения native указателя в этих полях, должен измениться соответствующим способом. Вместо доступа к полю типа int, native код должен обращаться к полю типа long. Эти изменения требуются для JNI вызовов, использующихся для получения поля ID, и для set и get JNI вызовов, использующихся для доступа к полям, например:

старые 32-разрядные:

 fid = (*env)->GetFieldID(env,cls,"my_handle","I");

 rc = (*env)->SetIntField(fid,obj,fid,my_handle);

 my_handle = (*env)->GetIntField(env,obj,fid);

 

новые 64-разрядные:

 fid = (*env)->GetFieldID(env,cls,"my_handle","J");

 rc = (*env)->SetLongField(fid,obj,fid,my_handle);

 my_handle = (*env)->GetLongField(env,obj,fid);

Если приложение Java должно поддерживаться и в 32-разрядной и в 64-разрядной средах, то очень желательно, чтобы отдельный набор исходного native кода и отдельный набор исходного кода Java оставался таким, чтобы можно было компилировать его в любой среде без возникновения каких-либо препятствий при поддержке другой среды. Одно привлекательное суждение состоит в том, чтобы сделать поля Java, хранящие значения native указателей, ссылками объекта Java. Поскольку ссылки на Java объект в действительности указатели, поле будет автоматически иметь требуемый для каждой среды размер. Проблема с этим суждением в том, что ссылка на объект всегда должна указывать на существующий Java объект. Иначе впоследствие возникнут проблемы при сборе "мусора".

Если это приемлемо, то самый простой способ - изменить тип всех int полей, хранящих значения native указателя, на long и сделать соответствующие изменения в native коде. Однако, препятствием этому способу будет наличие объектов в 32-разрядной среде с 64-разрядными полями типа long, когда вполне достаточно 32-разрядного int. Это может быть приемлемо в зависимости от характера Java приложения, количества создаваемых объектов и т.п. Если же подобные изменения 32-разрядной реализации не приемлемы, следует использовать более сложный способ.

Метод требует условного объявления Java полей, хранящих значения native указателя, а также native кода, использующегося для доступа к этим полям. В то время как С с готовностью предоставляет различные технологии - условные секции, макросы и т.п. для достижения этого в native коде, исходный код Java может вызывать проблемы. Чтобы достичь требуемой предварительной обработки кода Java, можно воспользоваться пре-процессором типа m4, но более опрятное решение состоит в использовании утилиты munge, которую Sun включила как часть кода JVM. Утилита munge - Java программа, которая использутся в процессе компиляции исходных файлов Java, обеспечивает условную компиляцию частей этих файлов.

Следующий пример показывает как исходники Java и native кода могут быть изменены в 32\64-разрядных средах

Пример оригинального Java кода, используемого в 32-разрядной среде:

class example {

 /* Java значение */

   int i;                 

 /* Поле используется для хранения native указателя */              

   private int my_handle;

 /* ссылки на другие Java объекты... */

   String str;

}

Пример оригинального native кода, используемого в 32-разрядной среде:

fid = (*env)->GetFieldID(env,cls,"my_handle","I");

rc = (*env)->SetIntField(fid,obj,fid,my_handle);

my_handle = (*env)->GetIntField(env,obj,fid);

Далее идет пример нового Java кода, который располагался бы в файле .javam. Утилита munge используется, чтобы превратить файл .javam в файл .java с тем же самым именем, готовый в дальнейшем к компиляции с помощью javac:

class example {

  int i;

/*if[64bit]

  private long my_handle; 

  else[64bit]*/

  private int my_handle;

/*end[64bit*/

  String str;

}

При 32-разрядной компиляции исходный .javam будет преобразован в следующий .java файл:

class example {

  int i;

  private int my_handle; 

  String str;           

}

а при 64-разрядной компиляции получим такой .java файл:

class example {

  int i;

  private long my_handle;

  String str;

}

Native код может быть условно откомпилирован следующим образом:

#if defined(_WIN64)

  fid = (*env)->GetFieldID(env,cls,"my_handle","J");

  rc = (*env)->SetLongField(fid,obj,fid,my_handle);

  my_handle = (*env)->GetLongField(env,obj,fid);

#else

  fid = (*env)->GetFieldID(env,cls,"my_handle","I");

  rc = (*env)->SetIntField(fid,obj,fid,my_handle);

  my_handle = (*env)->GetIntField(env,obj,fid);

#endif

Формирование 64-разрядных библиотек native кода

64-разрядный компилятор MS С/С++ для IA-64 использует много тех же самых опций компиляции, что и 32-разрядный компилятор MS C/C++ для 80х86, так что вопрос компиляции 64-разрядной версии библиотеки native кода состоит просто в модификации MAKEFILE. В некоторых случаях при 64-разрядной компиляции не нужно изменять опции 32-разрядной компиляции. При компиляции исходного кода в 64-разрядной среде хорошей идеей будет включение опции компиляции -Wp64. Это позволит провести дополнительную диагностику переноса, которая может помочь с ранней идентификацией проблем, вероятно возникнущих в 64-разрядной среде.

старые 32-разрядные опции: cl -MT -LD -W3 native.c



новые 64-разрядные опции: cl -MT -LD -W3 -Wp64 native.c

Другие соображения

64-разрядная среда требует дополнительной памяти для хранения увеличенных указателей и целых чисел и для обеспечения выравнивания данных. Это увеличивает требования к размеру стека и "кучи" объектов, необходимых Java приложению.Руководящий принцип такой - в 64-разрядной среде размеры стека и "кучи" должны быть удвоены по сравнению с 32-разрядной средой.

Поле fd в классе FileDescriptor, который имеет тип int в 32-разрядной среде, в 64-разрядной среде был изменен на long. Любой native код, обращающийся к этому полю, должен быть изменен в ссответствии с подходом, описанным выше.

Способностью в одно и то же время исполнять под 64-разрядной Windows 2000 обе JVM - и 32-разрядную, и 64-разрядную, IBM сделала более легким определение того, какую JVM ипользовать. Java команда -version была расширена, чтобы определить исполнемую JVM.

IBM-реализация 32-разрядной JVM:

>java -version



java version "1.3.0"

Java(TM) 2 Runtime Environment, Standard Edition

 (build 1.3.0)

Classic VM (build 1.3.0, J2RE 1.3.0 IBM Windows 32 build

 cndev-20000823 (JIT enabled: jitc))

IBM-реализация 64-разрядной JVM:

>java -version



java version "1.3.0"

Java(TM) 2 Runtime Environment, Standard Edition

 (build 1.3.0)

Classic VM (build 1.3.0, J2RE 1.3.0 IBM Windows 64 build

 cwdev-20000823 (JIT enabled: jitc))


Заключение

IBM-реализация 64-разрядной JVM для Windows 2000 исполняет 100% "чистые" Java приложения, не требуя их модификации, демонстрируя реальность парадигмы: "написать однажды, исполнять всюду". Java приложения, содержащие native код, требуют переноса, но это прямолинейная задача. Читайте всю возможную информацию о переносе 32-разрядного исходного кода в 64-разрядную Windows, определите потребность вашего Java приложения, определите подход для native кода, и затем переходите к IBM-реализации 64-разрядной JVM для Winows 2000.



Литература по Windows 2000