32-битные окна java: определение максимальной доступной кучи при запуске JRE

Наше приложение Java (которое мы хотим сохранить совместимым с 32-разрядными компьютерами с Windows, следовательно, с использованием 32-разрядной JRE) требует много памяти, и мы хотели бы предоставить ему максимально доступную память при запуске JRE (с использованием параметра Xmx).

Хотя теоретически 32-битные системы дают 4 Гбайт выделяемой памяти, мы поняли, что, учитывая управление памятью Windows, оно более ограничено самым большим непрерывным доступным блоком памяти. Небольшое тестирование показало, что значение 1024 МБ, скорее всего, будет работать на большинстве компьютеров.

Сейчас мы изучаем лучший подход для выделения самой большой памяти кучи, доступной при запуске приложения: есть ли какой-либо подход, кроме метода пробной ошибки (например, запустить JRE с 4 ГБ, а затем уменьшать, пока он не сработает )?

Кроме того, мы запускаем наше приложение с помощью Launch4J: предоставит ли это дополнительные параметры для определения максимально возможной памяти кучи?


person Tom    schedule 02.11.2014    source источник
comment
32 бита дают вам 2 ГБ. Единственный способ выделить - это написать сценарий, который пытается выделить разные суммы через -Xmx и продолжить, если процесс завершится с кодом ошибки. Имейте в виду, что если вы выделите максимальный объем памяти, в ОС закончится виртуальная память, которая вам может не понадобиться. Обратите внимание: максимальный объем памяти, доступной для выделения, зависит от максимального непрерывного блока виртуальной памяти.   -  person bestsss    schedule 02.11.2014
comment
Еще одно замечание: вы не хотите, чтобы java когда-либо использовала подкачку, полная сборка мусора, запускающая подкачку, может буквально занять несколько минут.   -  person bestsss    schedule 02.11.2014
comment
Спасибо. И есть ли способ использовать Launch4J для написания сценария испытаний или, по крайней мере, обнаружения проблем с памятью и отображения элегантного сообщения об ошибке?   -  person Tom    schedule 02.11.2014
comment
Я ничего не знаю о Launch4j, мы написали собственный лаунчер на C ++ (загружает jvm.dll и т. Д.). Вы просто не можете запустить java с большой кучей, так что вы можете это точно определить.   -  person bestsss    schedule 02.11.2014


Ответы (1)


Вот код win32, который я использую для этого в наших пусковых установках на основе JNI - оригинальным вдохновением для этого подхода был веб-сайт, который я больше не могу найти - надеюсь, он будет полезен (я понятия не имею, есть ли у Launch4J что-то вроде это - если они этого не делают, вы должны попросить их добавить это ;-)). Я использую 62 для permGenMB. maxHeapMB - это максимальный размер, который вы собираетесь запросить (768, 1024) - функция getMaxHeapAvailable () вернет размер ниже этого предела, который машина может фактически выделить для вас.

Обратите внимание, что win32 имеет ~ 3 ГБ адресуемой памяти, но вы не сможете получить такой большой кусок - JVM требует, чтобы память была в одном выделяемом блоке - на вашем компьютере может быть доступно 5 ГБ ОЗУ, но если самый большой В открытом блоке всего 1 ГБ, тогда это максимум, который может получить JVM.

static const DWORD NUM_BYTES_PER_MB = 1024 * 1024;

bool canAllocate(DWORD bytes)
{
    LPVOID lpvBase;

    lpvBase = VirtualAlloc(NULL, bytes, MEM_RESERVE, PAGE_READWRITE);
    if (lpvBase == NULL) return false;

    VirtualFree(lpvBase, 0, MEM_RELEASE);

    return true;
}

int getMaxHeapAvailable(int permGenMB, int maxHeapMB)
{
    DWORD       originalMaxHeapBytes = 0;
    DWORD       maxHeapBytes = 0;
    int         numMemChunks = 0;
    SYSTEM_INFO     sSysInfo;
    DWORD       maxPermBytes = permGenMB * NUM_BYTES_PER_MB;     // Perm space is in addition to the heap size
    DWORD       numBytesNeeded = 0;

    GetSystemInfo(&sSysInfo);

    // jvm aligns as follows: 
    // quoted from size_t GenCollectorPolicy::compute_max_alignment() of jdk 7 hotspot code:
    //      The card marking array and the offset arrays for old generations are
    //      committed in os pages as well. Make sure they are entirely full (to
    //      avoid partial page problems), e.g. if 512 bytes heap corresponds to 1
    //      byte entry and the os page size is 4096, the maximum heap size should
    //      be 512*4096 = 2MB aligned.

    // card_size computation from CardTableModRefBS::SomePublicConstants of jdk 7 hotspot code
    int card_shift  = 9;
    int card_size   = 1 << card_shift;

    DWORD alignmentBytes = sSysInfo.dwPageSize * card_size;

    maxHeapBytes = maxHeapMB * NUM_BYTES_PER_MB + 50*NUM_BYTES_PER_MB; // 50 is an overhead fudge factory per https://forums.oracle.com/forums/thread.jspa?messageID=6463655 (they had 28, I'm bumping it 'just in case')

    // make it fit in the alignment structure
    maxHeapBytes = maxHeapBytes + (maxHeapBytes % alignmentBytes);
    numMemChunks = maxHeapBytes / alignmentBytes;
    originalMaxHeapBytes = maxHeapBytes;

    // loop and decrement requested amount by one chunk
    // until the available amount is found
    numBytesNeeded = maxHeapBytes + maxPermBytes; 
    while (!canAllocate(numBytesNeeded) && numMemChunks > 0) 
    {
        numMemChunks --;
        maxHeapBytes = numMemChunks * alignmentBytes;
        numBytesNeeded = maxHeapBytes + maxPermBytes;
    }

    if (numMemChunks == 0) return 0;

    // we can allocate the requested size, return it now
    if (maxHeapBytes == originalMaxHeapBytes) return maxHeapMB;

    // calculate the new MaxHeapSize in megabytes
    return maxHeapBytes / NUM_BYTES_PER_MB;
}
person Kevin Day    schedule 02.11.2014