JNI_ENOMEM из JNI_CreateJavaVM при вызове DLL, которая использует JNI из VB6 - PullRequest
1 голос
/ 03 августа 2010

Я работаю в устаревшей системе, в которой есть приложение VB6, которое должно вызывать код Java. Решение, которое мы используем, состоит в том, чтобы приложение VB вызывало DLL C ++, которая использует JNI для вызова кода Java. Немного странно, но на самом деле это работает довольно хорошо. Тем не менее, я перехожу на новую коробку разработчика, и я столкнулся с серьезной проблемой с этим. Встроенное приложение VB отлично работает на новом компьютере, но когда я пытаюсь запустить его из VB, DLL не может загрузить виртуальную машину, получая код возврата -4 (JNI_ENOMEM) из JNI_CreateJavaVM.

И встроенное приложение, и VB вызывают одну и ту же dll, и я пробовал это как на Java 1.5, так и на 1.6. Я попробовал предложения здесь (перенаправление stdout и stderr в файлы, добавление опции vfprint, добавление опции -Xcheck: jni), но безрезультатно. Я не могу получить дополнительную информацию из JVM. Насколько я могу судить, новый блок настроен почти так же, как старый (установленное программное обеспечение, путь, Classpath и т. Д.), И оба работают под управлением одного и того же выпуска Windows Server 2003. Новый компьютер - x64 коробка с большим объемом памяти (4 ГБ, а не 2 ГБ), но она работает под управлением 32-битной Windows.

Какие-либо предложения или идеи о том, что еще посмотреть? Переписать все это более разумным способом - не вариант - мне нужно найти способ заставить dll заставить загрузку jvm, не думая, что ей не хватает памяти. Любая помощь будет высоко ценится.

Ответы [ 3 ]

1 голос
/ 13 июля 2012

К вашему сведению - я нашел следующую чрезвычайно полезную статью: https://forums.oracle.com/forums/thread.jspa?messageID=6463655

Я собираюсь повторить здесь какой-то безумно полезный код, потому что я не уверен, что доверяю Oracle в сохранении вышеупомянутого форума.

Когда я настраиваю свою JVM, я использую вызов getMaxHeapAvailable (), а затем соответствующим образом устанавливаю пространство кучи (-Xmxm) - отлично работает для рабочих станций с меньшим объемом доступной оперативной памяти, без необходимости наказывать пользователей большим объемом оперативной памяти.

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;

    // 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 + 50*NUM_BYTES_PER_MB) && numMemChunks > 0) // 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')
    {
        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;
}
1 голос
/ 05 августа 2010

ОК, я понял это.Как указывает kschneid, JVM требуется довольно большой непрерывный кусок памяти внутри области памяти приложения.Поэтому я использовал утилиту sysinternals VMMap, чтобы посмотреть, как выглядит память VB.На самом деле не было большой части доступной памяти, и было несколько библиотек, принадлежащих Visio, которые были загружены в местах, которые, казалось, были предназначены для фрагментации памяти.Оказывается, когда я установил Visio на новую машину, он автоматически установил надстройку Visio UML в VB.Поскольку я не использую эту надстройку, я отключил ее.При отключенной надстройке имелся большой непрерывный кусок свободной памяти, и теперь JVM загружается просто отлично.

0 голосов
/ 05 августа 2010

У меня была та же проблема, описанная "klaus", и я прочитал "http://support.microsoft.com/kb/126962".. Изменил реестр, как описано в упомянутой статье. Я преувеличил свое изменение на:"% SystemRoot% \ system32 \ csrss.exe ObjectDirectory = \ Windows SharedSection = 3072,3072,3072 Windows = Вкл. SubSystemType = Windows ServerDll = baserv, 1 ServerDll = winsrv: UserServerDllInitialization, 3 ServerDll = winsrv: ConServerDllInitialization, 2 ProfileControl = Off MaxRequestThreads = 16 "*

Поле для просмотра "SharedSection = 3072,3072,3072". Это решило мою проблему, но у меня могут быть побочные эффекты из-за этого изменения.

...