调用从VB6使用JNI的dll时,从JNI_CreateJavaVM调用JNI_ENOMEM

调用从VB6使用JNI的dll时,从JNI_CreateJavaVM调用JNI_ENOMEM,java,memory,vb6,java-native-interface,visio,Java,Memory,Vb6,Java Native Interface,Visio,我在一个遗留系统上工作,该系统有一个需要调用Java代码的VB6应用程序。我们使用的解决方案是让VB应用程序调用一个C++ JLL,使用JNI调用java代码。有点怪,但实际上效果很好。然而,我正在移动到一个新的开发盒,我刚刚遇到了一个严重的问题。构建的VB应用程序在新框上运行良好,但当我尝试从VB运行它时,dll无法加载VM,从JNI_CreateJavaVM获得-4(JNI_ENOMEM)的返回代码 构建的应用程序和VB都调用完全相同的dll,我已经用Java1.5和1.6尝试过了。我尝试过

我在一个遗留系统上工作,该系统有一个需要调用Java代码的VB6应用程序。我们使用的解决方案是让VB应用程序调用一个C++ JLL,使用JNI调用java代码。有点怪,但实际上效果很好。然而,我正在移动到一个新的开发盒,我刚刚遇到了一个严重的问题。构建的VB应用程序在新框上运行良好,但当我尝试从VB运行它时,dll无法加载VM,从JNI_CreateJavaVM获得-4(JNI_ENOMEM)的返回代码

构建的应用程序和VB都调用完全相同的dll,我已经用Java1.5和1.6尝试过了。我尝试过这些建议(将stdout和stderr重定向到文件、添加vfprint选项、添加-Xcheck:jni选项),但没有效果。我似乎无法从jvm中获得任何其他信息。据我所知,新盒子的配置与旧盒子基本相同(已安装的软件、路径、类路径等),并且都运行相同版本的WindowsServer2003。这台新机器是一台拥有更多内存(4GB而不是2GB)的x64机器,但它运行的是32位Windows


还有什么其他的建议或想法吗?以一种更理智的方式重写整个过程不是一种选择——我需要找到一种方法让dll加载jvm,而不必考虑内存不足。任何帮助都将不胜感激。

好的,我已经弄明白了。正如kschneid指出的,JVM需要在应用程序的内存空间中有一个相当大的连续内存块。因此,我使用sysintransevmmap实用程序来查看VB的内存是什么样子的。事实上,没有大的内存块可用,而且有一些属于Visio的库被加载到似乎是为了分割内存而设计的位置。事实证明,当我在新机器上安装Visio时,它会自动将VisioUML加载项安装到VB中。因为我不使用此加载项,所以禁用了它。禁用外接程序后,有一大块连续的可用内存可用,现在JVM可以正常加载。

我遇到了“klaus”和“read”所描述的相同问题。如上述文章所述更改了注册表。我将更改示例为:“%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=30723072 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:servaterdlinitialization,2 ProfileControl=Off MaxRequestThreads=16”


要查看的字段是“SharedSection=30723072”。它解决了我的问题,但由于这一变化,我可能会有副作用。

仅供参考-我发现以下文章非常有用:

我将在这里重复一些非常有用的代码,因为我不确定我是否相信Oracle会保留上述论坛

当我设置JVM时,我使用对getMaxHeapAvailable()的调用,然后相应地设置堆空间(-Xmxm)-对于可用RAM较少的工作站非常有效,而不必惩罚具有大量RAM的用户

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;
}
bool-canaldocate(DWORD字节)
{
lpvBase;
lpvBase=VirtualAlloc(空,字节,内存保留,页面读写);
if(lpvBase==NULL)返回false;
VirtualFree(lpvBase,0,内存释放);
返回true;
}
int getMaxHeapAvailable(int permGenMB,int maxHeapMB)
{
DWORD ORIGINALMAXHEAPBETS=0;
DWORD maxHeapBytes=0;
int numemchunks=0;
系统信息;
DWORD maxPermBytes=permGenMB*NUM_BYTES_PER_MB;//Perm空间是堆大小之外的
DWORD NUMBYTESNEEED=0;
GetSystemInfo(和sSysInfo);
//jvm的对齐方式如下:
//引自jdk 7热点代码的size\t gencollector policy::compute\u max\u alignment():
//旧一代的卡片标记阵列和偏移阵列是
//也在操作系统页面中提交。请确保它们已完全满(到)
//避免部分页面问题),例如,如果512字节堆对应于1
//字节项,操作系统页面大小为4096,最大堆大小应为
//将512*4096=2MB对齐。
//从jdk 7热点代码的CardTableModRefBS::SomePublicConstants计算卡片大小
int card_shift=9;
int card_size=1 0)//50是一家制造软糖的工厂https://forums.oracle.com/forums/thread.jspa?messageID=6463655 (他们有28个,我只是“以防万一”碰一下)
{
numMemChunks——;
maxHeapBytes=nummechunks*alignmentBytes;
numBytesNeeded=maxHeapBytes+maxPermBytes;
}
如果(nummechunks==0)返回0;
//我们可以分配请求的大小,现在返回
if(maxHeapBytes==originalMaxHeapBytes)返回maxHeapMB;
//以MB为单位计算新的MaxHeapSize
返回maxHeapBytes/NUM\u BYTES\u PER\u MB;
}

您是否将设置最大堆和/或最大perm大小的选项传递给JVM?这两个大小的总和需要作为一个连续块,JVM才能启动。我尝试为-Xmx和-Xms传递各种值。直到52m,dll调用才导致VB退出(没有错误消息)。从53m开始,我得到了JNI_ENOMEM返回代码。当然有足够的内存可用,除非VB在DLL的内存分配方面做了一些非常奇怪的事情,否则应该有足够的连续内存可供具有53MB堆的JVM使用。这一切在我的旧开发设备上(以及我们使用过的所有其他机器上)都可以正常工作。这看起来不像是同一个问题。此问题的原因是进程地址空间碎片,而不是WIN32桌面堆空间不足。