I have created a JNI dll, invoked from a VBA Excel add-in to execute Java code. This works fine until I introduce the -Xmx parameter as a vm option.
Before creating the VM, I use a VirtualAlloc/Free loop to make sure the -Xmx parameter is not too large, decrementing it by 2MB each time until an allocable amount is found (thanks to [jurberg's post|http://forums.sun.com/thread.jspa?forumID=37&threadID=5220601]; my version, slightly modified, is posted below). I then pass that value to the VM via the -Xmx option.
This works great when I am using a
JRE 1.6 installed in the "C:\Program Files\Java\*jre1.6.0_xx"* directory. When the
same* JRE version is installed in the "C:\Program Files\Java\*jre6*" directory, JNI_CreateJavaVM fails with JNI_ENOMEM. Calling GetLastError returns 0 ("operation completed successfully"). Using a *1.5 JRE*, this also fails, returning JNI_ENOMEM, but GetLastError returns error code 6 ("the handle is invalid").
A little about my platform:
Windows XP Pro
Building JNI dll using Microsoft Visual C++ 2008 and 1.5 JDK
I have multiple JREs and JDKs installed on my system as this is a dev machine, but I have the same problem on a non-dev machine running XP Home.
Here is a snippet of my code used to create the vm:
// create the JNI structures
const int numOptions = args.size();
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[numOptions];
log("Creating JVM with parameters:");
int i = 0;
char * nextArg;
for (itr=args.begin(); itr != args.end(); itr++) {
nextArg = new char[(*itr).length() + 1];
strcpy(nextArg, (*itr).c_str());
options.extraInfo = NULL;
options[i++].optionString = nextArg;
log("\t" + string(nextArg));
}
vm_args.version = CRUSH_JNI_VERSION;
vm_args.options = options;
vm_args.nOptions = numOptions;
vm_args.ignoreUnrecognized = JNI_FALSE;
// load and initialize the Java VM, and return a JNI interface pointer
JNIEnv* env = NULL;
err = (*createVM)(&jvm, (void**)&env, &vm_args);
// err is -4 (JNI_ENOMEM) in the cases described above
Does anyone have any suggestions on what is going on here and how I might make this code stable for all 1.5 and 1.6 JREs, regardless of where they are installed?
Thanks in advance,
Sarah
---------------------------------------------------
Code to determine max -Xmx value:
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;
// 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;
}