Take the 2-minute tour ×

We ship a Java application whose memory demand can vary quite a lot depending on the size of the data it is processing. If you don't set the max VM (virtual memory) size, quite often the JVM quits with an GC failure on big data.

What we'd like to see, is the JVM requesting more memory, as GC fails to provide enough, until the total available VM is exhausted. e.g., start with 128Mb, and increase geometrically (or some other step) whenever the GC failed.

The JVM ("Java") command line allows explicit setting of max VM sizes (various -Xm* commands), and you'd think that would be designed to be adequate. We try to do this in a .cmd file that we ship with the application. But if you pick any specific number, you get one of two bad behaviors: 1) if your number is small enough to work on most target systems (e.g., 1Gb), it isn't big enough for big data, or 2) if you make it very large, the JVM refuses to run on those systems whose actual VM is smaller than specified.

How does one set up Java to use the available VM when needed, without knowing that number in advance, and without grabbing it all on startup?

share|improve this question
Please get your terminology straight: there is no such thing as "available VM" or "actual VM". Presumably you mean RAM. –  Michael Borgwardt Jul 20 '09 at 9:33
@Michael: perhaps you are confused by "JVM" and "VM" in the same sentence. JVM refers to the Java Virtual Machine. "VM" refers to the virtual memory provided by the OS: en.wikipedia.org/wiki/Virtual_memory I'm interested in the max VM that the OS will allow a process to use, which is usually the same value as the OS configures for total VM for all processes. –  Ira Baxter Jul 20 '09 at 9:41
Ah, now I get it. It's indeed confusing to have those identical abbreviations next to each other. –  Michael Borgwardt Jul 20 '09 at 9:52
The limited JVM memory options are a really big flaw in Java. While the better answers here are clever hacks, they are still just hacks. I would love to see more elegant memory management in Java 7. –  James McMahon Jul 21 '09 at 12:41
we have the same problem here with some apps; most cases it will process small sets of data and we would not like to give it that much memory, and then once in a while it needs much memory since it needs to process large sets. we have no solution so far, we will aim for creating a wrapper that checks the size of data to process and that starts up the java process with corresponding parameters...really just a bad hack solution, but we cant see any other way –  JohnSmith Aug 10 '10 at 10:10

14 Answers 14

You can also use the option: -XX:+AggressiveHeap

This according to the [documentation][1]:


[1]: http://java.sun.com/docs/hotspot/gc1.4.2/#4.2.2. AggressiveHeap|outline

share|improve this answer
Wow I have never seen these options before... this is great reading, thanks –  Newtopian Jul 20 '09 at 11:32
With this option, the algorithms attempt to use heaps nearly as large as the total physical memory. What happens if the user tries to run some other application? Perhaps the option is useful for server dedicated environments? –  zkarthik Jul 20 '09 at 11:35
Any -XX option is liable to change (or disappear) without warning. They are not a public API of the JVM and possibly you should avoid (if possible) bundling a public product with them –  oxbow_lakes Jul 20 '09 at 12:32
Indeed -XX:+AggressiveHeap does not seem to be available in 1.5 and later. –  Stephen C Jul 21 '09 at 5:58
@zkarthik I believe this option is really only useful for servers that do pretty much nothing other than run one Java VM. I think physical memory also excludes swap space. –  Sindri Traustason Jul 22 '09 at 12:17

The max VM sizes indeed answer to that need (it sets the max value, but the VM will take only necessary, step by step), but if you need several configurations, besides supplying different "cmd" files, I don't really see a way (though i'll search a bit more)

[edit] How about using a first program/script (or even another java program), which would check the available resources for the system, and then only call your program with the appropriate -Xm, according to what it retrieved from system ? That way it would adapt to machines, even if you don't know them before. Could be an idea...

[second edit] Ok, this has been proposed already by skaffman, my bad.

share|improve this answer
Not your bad, if you look at the timestamps your answer arrived sooner than his. However, the discussion seems to be going on around Brian's response, also not different than yours. Thanks for chiming in. –  Ira Baxter Jul 20 '09 at 9:59

I think you're out of luck :-( The -Xms and -Xmx options don't provide that flexibility.

So I think you will need to wrap your JVM invocation with a script that can determine the maximum amount of memory, and then set -Xmx appropriately (probably a .vbs script using WMI on Windows). Or perhaps it asks the users the first time it's run ?

A bit of a pain, I fear.

share|improve this answer
I can't imagine I'm the only guy on the planet with this problem. Are Sun/IBM truly that witless? –  Ira Baxter Jul 20 '09 at 9:19
The trouble with this is that if they are running other stuff on the machine, they will end up swapping to disk, slowing down their applications enormously –  oxbow_lakes Jul 20 '09 at 9:19
@Ira - why are they witless? Why would you expect an application to (paraphrasing) "need 1Gb, but if that's not available, then 500Mb will do"? If you can make do with 500Mb, why ask for a Gig? –  oxbow_lakes Jul 20 '09 at 9:21
There are so many issues like this it's untrue. No - you're not the only one. I tend to work in environments where each machine is the same (so this isn't an issue) or I have Java-aware users who are able to modify this parameter themselves. I think the complexity of your ultimate solution depends on your audience/clients :-) –  Brian Agnew Jul 20 '09 at 9:21
The request I want to make is, "Give a minimum allocation, then take memory as you need it up to the availble VM size", because I don't know what the data size demand is in advance. And no, I don't have the luck to have Java-aware customers, so asking them to choose is really not an option. –  Ira Baxter Jul 20 '09 at 9:26

One more option... I work on a launcher called WinRun4J, which allows you to specify a max heap size as a percentage of the available memory on the machine its running on (ie. it does a check for the amount of memory available and sets the -Xmx parameter dynamically on startup).

The INI option is "vm.heapsize.max.percent". There is also another option "vm.heapsize.preferred", which sets the -Xmx parameter as the maximum available memory on the machine up to this amount.

I believe some of the other launchers (eg. Launch4J, Janel) offer the same functionality.

share|improve this answer
launch4j.sourceforge.net indicates it does, I'm surprised this answer doesn't have more votes it sounds like a perfect solution and it's free... –  ShuggyCoUk Jul 21 '09 at 12:51

I think the easiest way to do this would be to launch the JVM via some wrapper application, which would check system resources to determine memory availability, and then launch the JVM with the appropriate -Xmx parameter.

The question then becomes how that wrapper would be written. It may even be possible for the wrapper app to itself be a JVM, although I don't think the API or system properties would expose the necessary information. Maybe a shell script or your choice could get the information.

share|improve this answer
For me the real question is why isn't this one of the standard JVM -XM* options? The problem was obvious to us about 30 seconds after we ran out of memory on the first application run. –  Ira Baxter Jul 20 '09 at 10:01
I can't say I've ever come across the need for this, although I do appreciate the usefulness in some situations. –  skaffman Jul 20 '09 at 10:05
Based on the dicussions, it appears I have to write a wrapper. Probably can't write it in Java. So now I need a non-Java something to run a Java program. That something is probably different under Windows than it is under Linux, etc. So much for "write once, run anywhere", it now "write once, configure differently everywhere". Takes some of the appeal out of using Java. –  Ira Baxter Jul 20 '09 at 10:07
This is actually a JVM option, -XX:+AggressiveHeap does automatically set -XM and more. See my answer to the question for more info. –  Sindri Traustason Jul 20 '09 at 10:30
@Ira: There are many platform-specific JVM options. Noone ever claimed that the JVM itself was cross-platform. –  skaffman Jul 20 '09 at 10:37

We have a small C application that we use for launching all of our Java applications via JNI. This allows us to:

  1. Have a meaningful process name (esp important under windows)
  2. Have our own icons (again, important for Windows)
  3. Dynamically build classpaths (we parse out the contents of the /lib file to auto-include all jars)

For our apps, we just hard code the heap limit, but you could easily dynamically configure max heap size based on available memory.

This sort of little app is actually pretty easy to do (it's one of the easiest things to do with JNI). A good starting point would be the source for the JDK (there's a sub-folder for java.exe itself that you can use - that's what we did). Most folks are quite surprised to find that java.exe is a little tiny application (< 200 lines of code) that just invokes JNI and passes command line arguments in (heck, even the use of a method called main() is pretty optional once you start launching things yourself).

Here's code that not only starts up the JVM, etc... but also determines the maximum heap space based on available RAM of the computer. This is a lot of code for an SO post, and it's not at all pretty - but this is battle hardened code - it's been used for almost a decade over many hundreds of installs, etc... Enjoy :

#include <windows.h>
#include <jni.h>
#include <string>
#include <sstream>
using namespace std;

#define STARTUP_CLASS "some/path/to/YourStartupClass"

void vShowError(string sErrorMessage);
void vShowJREError(string sErrorMessage);
void vShowLastError(string sErrorMessage);
void vDestroyVM(JNIEnv *env, JavaVM *jvm);
void vAddOption(string& sName);
string GetClassPath(string root);
string GetJREPath();
int getMaxHeapAvailable(int permGenMB, int maxHeapMB);

JavaVMOption* vm_options;
int mctOptions = 0;
int mctOptionCapacity = 0;

boolean GetApplicationHome(char *buf, jint sz);

typedef jint (CALLBACK *CreateJavaVM)(JavaVM
**pvm, JNIEnv **penv, void *args);

boolean PathExists(string &path)
    DWORD dwAttr = GetFileAttributes(path.c_str());
    if (dwAttr == 0xffffffff)
        return FALSE;
        return TRUE;

// returns TRUE is there was an exception, FALSE otherwise
BOOL GetExceptionString(JNIEnv* jenv, string &result)
    jthrowable ex;

    if (NULL != (ex = jenv->ExceptionOccurred())) {
        // clear exception 

        jmethodID gmID = jenv->GetMethodID( 

        jstring jerrStr = (jstring)jenv->CallObjectMethod(ex,gmID);
        // now you can look at the error message string 

        if (jerrStr != NULL){ // make sure getMessage() didn't return null
            const char *errStr = jenv->GetStringUTFChars(jerrStr,0);
            result = errStr;
            jenv->ReleaseStringUTFChars(jerrStr, errStr);
        } else {
            result = "null";

        return TRUE;
    } else {
        return FALSE;

BOOL GetJRESystemProperty(JNIEnv *env, string propname, string &propval, string &errmessage)
    // now check for minimum JRE version requirement
    jclass cls = env->FindClass("java/lang/System");
    if (cls == NULL){
        errmessage = "Unable to interact with Java Virtual Machine - please visit www.java.com and confirm that your Java installation is valid.";
        return FALSE;

    jmethodID mid = env->GetStaticMethodID(cls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
    if (mid == NULL){
        errmessage = "Unable to obtain Java runtime system properties - please visit www.java.net and confirm that your Java installation is valid.";
        return FALSE;

    jstring propName = env->NewStringUTF( propname.c_str() );
    jstring result = (jstring) env->CallStaticObjectMethod(cls, mid, propName);
    const char* utfResult = env->GetStringUTFChars( result, NULL );

    if (utfResult == NULL){
        errmessage = "Unable to obtain Java runtime system property " + propname + " - please visit www.java.net and confirm that your Java installation is valid.";
        return FALSE;

    propval = utfResult;
    env->ReleaseStringUTFChars( result, utfResult );

    return TRUE;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {

    JNIEnv *env;
    JavaVM *jvm;
    jint jintVMStartupReturnValue;
    jclass jclassStartup;
    jmethodID midStartup;

    // Path Determination

    // --- application home
    char home[2000];
    if (!GetApplicationHome(home, sizeof(home))) {
        vShowError("Unable to determine application home.");
        return 0;
    string sAppHome(home);
    string sOption_AppHome = "-Dapplication.home=" + sAppHome;

    string sJREPath = GetJREPath();

    // --- VM Path
    string sRuntimePath = sJREPath + "\\bin\\client\\"; // must contain jvm.dll
    string sJVMpath = sRuntimePath + "jvm.dll";

    // --- boot path
    string sBootPath = sJREPath + "\\lib";
    string sOption_BootPath = "-Dsun.boot.class.path=" + sBootPath;

    // --- class path
    //string sClassPath = sAppHome + "\\lib;" + sAppHome + "\\lib\\" + APP_JAR + ";" + sAppHome + "\\lib\\log4j-1.2.7.jar";

    string cpRoot = sAppHome + "\\";
    string sClassPath = GetClassPath(cpRoot);

    string sOption_ClassPath = "-Djava.class.path=" + sClassPath;

    string sOption_JavaLibraryPath = "-Djava.library.path=" + sAppHome + "\\lib";

    int maxHeapBM = 768;

    int argStart = 1; // the first argument passed in that should be passed along to the JVM
    if(__argc > 1){
        string maxheapstr = __argv[1];
        if (maxheapstr.substr(0, 9).compare("/maxheap=") == 0){
            maxheapstr = maxheapstr.substr(9);
            maxHeapBM = atoi(maxheapstr.c_str());

    // we now use adaptive max heap size determination - we try for 768MB of heap, but if we don't get it, we can back off and use less instead of failing the launch
    // note: we had problems going for 1024 heap at TrueNorth - it would throttle back to 848 and fail with error -4 no matter what I did
    int maxHeapMB = getMaxHeapAvailable(62, maxHeapBM);
    stringstream ss;
    ss << "-Xmx";
    ss << maxHeapMB;
    ss << "m";
    string sOption_HeapSpace = ss.str();

    string sOption_PermSize = "-XX:MaxPermSize=62m";

    string sOption_HeapDump = "-XX:+HeapDumpOnOutOfMemoryError";

    if (strstr(szCmdLine, "/launcher_verbose") != NULL){
        string msg = "App Home = ";
        msg += sAppHome;
        msg += "\nJRE Path = ";
        msg += sJREPath;
        msg += "\nRuntime Path = ";
        msg += sRuntimePath;
        msg += "\nClass Path = ";
        msg += sClassPath;
        msg += "\nHeap argument = ";
        msg += sOption_HeapSpace;
        msg += "\nPermsize argument = ";
        msg += sOption_PermSize;
        msg += "\nHeap dump = ";
        msg += sOption_HeapDump;
        msg += "\njava.library.path = ";
        msg += sOption_JavaLibraryPath;
        msg += "\nCommand line = ";
        msg += szCmdLine;

        FILE *f = fopen("launcher.txt", "w");
        fprintf(f, "%s", msg.c_str());

        MessageBox(0, msg.c_str(), "Launcher Verbose Info", MB_OK);


    // setup VM options
    // vAddOption(string("-verbose"));


    // initialize args
    JavaVMInitArgs vm_args;
    vm_args.version = 0x00010002;
    vm_args.options = vm_options;
    vm_args.nOptions = mctOptions;
    vm_args.ignoreUnrecognized = JNI_TRUE;

    // need to diddle with paths to ensure that jvm can find correct libraries - see http://www.duckware.com/tech/java6msvcr71.html
    string sBinPath = sJREPath + "\\bin";
    char originalCurrentDirectory[4096];
    GetCurrentDirectory(4095, originalCurrentDirectory);


    // Dynamic binding to SetDllDirectory()
    typedef BOOL (WINAPI *LPFNSDD)(LPCTSTR lpPathname);
    HINSTANCE hKernel32 = GetModuleHandle("kernel32");
    LPFNSDD lpfnSetDllDirectory = (LPFNSDD)GetProcAddress(hKernel32, "SetDllDirectoryA");
    if (lpfnSetDllDirectory){

    // load jvm library
    HINSTANCE hJVM = LoadLibrary(sJVMpath.c_str());

    if (lpfnSetDllDirectory){

    if( hJVM == NULL ){
        vShowJREError("Java does not appear to be installed on this machine.  Click OK to go to www.java.com where you can download and install Java");
        return 0;

    // try to start 1.2/3/4 VM
    // uses handle above to locate entry point
    CreateJavaVM lpfnCreateJavaVM = (CreateJavaVM)
    GetProcAddress(hJVM, "JNI_CreateJavaVM");
    jintVMStartupReturnValue = (*lpfnCreateJavaVM)(&jvm, &env, &vm_args);

    // test for success
    if (jintVMStartupReturnValue < 0) {
        stringstream ss;
        ss << "There is a problem with the 32 bit Java installation on this computer (";
        ss << jintVMStartupReturnValue;
        ss << ").  Click OK to go to www.java.com where you can download and re-install 32 bit Java";

        // I don't think we should destroy the VM - it never was created...
        //vDestroyVM(env, jvm);
        return 0;

    //now check for minimum jvm version 
    string version = "";
    string errormsg = "";
    if (!GetJRESystemProperty(env, "java.specification.version", version, errormsg)){
        vDestroyVM(env, jvm);
        return 0;

    double verf = atof(version.c_str());
    if (verf < 1.599f){
        string sErrorMessage = "This application requires Java Runtime version 1.6 or above, but your runtime is version " + version + "\n\nClick OK to go to www.java.com and update to the latest Java Runtime Environment";
        vDestroyVM(env, jvm);
        return 0;

    // find startup class
    string sStartupClass = STARTUP_CLASS;
    // notice dots are translated to slashes
    jclassStartup = env->FindClass(sStartupClass.c_str());
    if (jclassStartup == NULL) {
        string sErrorMessage = "Unable to find startup class [" + sStartupClass + "]";
        vDestroyVM(env, jvm);
        return 0;

    // find startup method
    string sStartupMethod_Identifier = "main";
    string sStartupMethod_TypeDescriptor =
    midStartup = 
    if (midStartup == NULL) {
        string sErrorMessage =
            "Unable to find startup method ["
            + sStartupClass + "."
            + sStartupMethod_Identifier
            + "] with type descriptor [" +
            sStartupMethod_TypeDescriptor + "]";
        vDestroyVM(env, jvm);
        return 0;

    // create array of args to startup method
    jstring jstringExampleArg;
    jclass jclassString;
    jobjectArray jobjectArray_args;

    jstringExampleArg = env->NewStringUTF("example string");
    if (jstringExampleArg == NULL){
        vDestroyVM(env, jvm);
        return 0;
    jclassString = env->FindClass("java/lang/String");
    jobjectArray_args = env->NewObjectArray(__argc-argStart, jclassString, jstringExampleArg);
    if (jobjectArray_args == NULL){
        vDestroyVM(env, jvm);
        return 0;

    int count;
    for (count = argStart; count < __argc; count++){
        env->SetObjectArrayElement(jobjectArray_args, count-1, env->NewStringUTF(__argv[count]));

    // call the startup method -
    // this starts the Java program
    env->CallStaticVoidMethod(jclassStartup, midStartup, jobjectArray_args);

    string errstr;
    if (GetExceptionString(env, errstr)){

    // attempt to detach main thread before exiting
    if (jvm->DetachCurrentThread() != 0) {
        vShowError("Could not detach main thread.\n");

    // this call will hang as long as there are
    // non-daemon threads remaining

    return 0;


void vDestroyVM(JNIEnv *env, JavaVM *jvm)
    if (env->ExceptionOccurred()) {

void vShowError(string sError) {
    MessageBox(NULL, sError.c_str(), "Startup Error", MB_OK);

void vShowJREError(string sError) {
    ShellExecute(NULL, "open", "http://www.java.com", NULL, NULL, SW_SHOWNORMAL);

/* Shows an error message in an OK box with the
system GetLastError appended in brackets */
void vShowLastError(string sLocalError) {
    LPVOID lpSystemMsgBuf;
                    (LPTSTR) &lpSystemMsgBuf, 0, NULL );
    string sSystemError = string((LPTSTR)lpSystemMsgBuf);
    vShowError(sLocalError + " [" + sSystemError + "]");

void vAddOption(string& sValue) {
    if (mctOptions >= mctOptionCapacity) {
        if (mctOptionCapacity == 0) {
            mctOptionCapacity = 3;
            vm_options = (JavaVMOption*)malloc(mctOptionCapacity * sizeof(JavaVMOption));
        } else {
            JavaVMOption *tmp;
            mctOptionCapacity *= 2;
            tmp = (JavaVMOption*)malloc(mctOptionCapacity * sizeof(JavaVMOption));
            memcpy(tmp, vm_options, (mctOptions-1) * sizeof(JavaVMOption));
            vm_options = tmp;
    vm_options[mctOptions-1].optionString = (char*)sValue.c_str();

/* If buffer is "c:\app\bin\java",
* then put "c:\app" into buf. */
jboolean GetApplicationHome(char *buf, jint sz) {
    char *cp;
    GetModuleFileName(0, buf, sz);
    *strrchr(buf, '\\') = '\0';
    if ((cp = strrchr(buf, '\\')) == 0) {
        // This happens if the application is in a
        // drive root, and there is no bin directory.
        buf[0] = '\0';
        return JNI_FALSE;
    return JNI_TRUE;

string GetClassPath(string root){
    string rootWithBackslash = root;

    if (rootWithBackslash[rootWithBackslash.length()-1] != '\\')
        rootWithBackslash += "\\";

    string cp = rootWithBackslash + "classes\\"; //first entry in the cp

    string libPathWithBackslash = rootWithBackslash + "lib\\";

    // now find all jar files...
    string searchSpec = libPathWithBackslash;

    searchSpec = libPathWithBackslash + "*.jar";

    WIN32_FIND_DATA fd;
    HANDLE find = FindFirstFile(searchSpec.c_str(), &fd); 
    while (find != NULL){
        cp += ";";
        cp += libPathWithBackslash;
        cp += fd.cFileName;
        if (!FindNextFile(find, &fd)){
            find = NULL;

    return cp;

string GetJREPath(){

    // first, check for JRE in application directory
    char home[2000];
        vShowError("Unable to determine application home.");
        return 0;
    string sJREPath(home);
    sJREPath += "\\jre";

    if (PathExists(sJREPath)){
        return sJREPath;

/* - don't check JAVA_HOME - it may be incorrect...
    // next, check the JAVA_HOME environment variable
    GetEnvironmentVariable("JAVA_HOME", home, sizeof(home));
    sJREPath = home;

    if (PathExists(sJREPath)){
        return sJREPath;


    // next, check registry
    HKEY hKeyJRERoot;
    HKEY hKeyJREInstance;
    DWORD dwType;
    DWORD dwSize;
    BYTE *pData;
    string valueName;
    string value;
    LONG regRslt;

    sJREPath = "";

    regRslt = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment", 0, KEY_READ, &hKeyJRERoot);

    if (regRslt == ERROR_SUCCESS){

        valueName = "CurrentVersion";

        regRslt = RegQueryValueEx(hKeyJRERoot, valueName.c_str(), NULL, &dwType, NULL, &dwSize);

        if (regRslt == ERROR_SUCCESS){
            pData = (BYTE *)malloc(dwSize);

            value = "";
            regRslt = RegQueryValueEx(hKeyJRERoot, valueName.c_str(), NULL, &dwType, pData, &dwSize);

            if (regRslt == ERROR_SUCCESS){
                value = (LPCSTR)pData;


            if (value != ""){

                regRslt = RegOpenKeyEx(hKeyJRERoot, value.c_str(), 0, KEY_READ, &hKeyJREInstance);

                if (regRslt == ERROR_SUCCESS){
                    valueName = "JavaHome";
                    value = "";

                    regRslt = RegQueryValueEx(hKeyJREInstance, valueName.c_str(), NULL, &dwType, NULL, &dwSize);

                    if (regRslt == ERROR_SUCCESS){
                        pData = (BYTE *)malloc(dwSize);

                        regRslt = RegQueryValueEx(hKeyJREInstance, valueName.c_str(), NULL, &dwType, pData, &dwSize);

                        if (regRslt == ERROR_SUCCESS){
                            value = (LPCSTR)pData;
                            sJREPath = value;


    return sJREPath;


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;


    // 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;
share|improve this answer
Hi kevin. Can you post some bits of that code now.. I know it was long time back!! –  ayush Nov 8 '12 at 10:25
ok - code is posted - enjoy. –  Kevin Day Nov 10 '12 at 0:26

if you have a lot of time on your hand you could try the following :

Try to obtain what is the needed memory vs input dataset. With this you can split processing in a different set of classes and create a new JVM process to actually process the data. Basically a Manager and a Worker. The Manager would do a basic analysis on the demanded dataset and spawn a Worker with the appropriate memory requirements. You could probably also set your Manager to be aware of the environment and warn the user when they are trying to operate on a dataset their machine cannot handle.

This is pretty much an extension on the answer provided by skaffman but will happen all within the same app as far as the user is concerned.

share|improve this answer
Doesn't work for us. That analysis to decide how much VM we need, pretty much uses the amount of VM needed. This is true for a lot of complicated computations. –  Ira Baxter Mar 12 '11 at 23:04

There is two options in the virtual machine arguments that can be used : -Xms to set the memory size at startup and -Xmx to set the maximum memory size...

You can set a low startup memory and a big maximum one, so the VM will allocate new memory only if needed.

share|improve this answer
I think the problem is that if you set -Xmx large enough, then it's greater than the memory available and the JVM exits –  Brian Agnew Jul 20 '09 at 9:16
If you set -Xmx larger that the currently available VM, the JVM chokes on startup and refuses to run. The problem is that the system on which the applicaiton is run isn't ours, and we don't know what that target JVM size is. –  Ira Baxter Jul 20 '09 at 9:16
ok I didn't understood the question... the question is how to create a launch script if you don't know the max available memory. –  Vinze Jul 20 '09 at 9:33
-Xmx only virtually allocates and can be well in excess of physical memory. Why was this response voted down? –  Xepoch Apr 28 '10 at 23:06

I do not think either the Sun or IBM JVM can do this (I know that the AS/400 one can, but that is most likely not relevant to you).

I would suggest using Java WebStart (and before you discard this, then notice that it has been updated with Java 6 u 10 and is much better suited for launching "local" applications and applet) since it allows you to provide a "small instance", "larger instance", "gigantic instance" as links/icons.

You will most likely look into the "inject application in webstart cache" and "offline"options.

share|improve this answer

I don't think you can do what you are trying to do; instead you'll have to ship instructions specific to your customers, their systems and their demands of how they can modify your .cmd file to allow for more memory.

Of course, if your product is aimed at very non-technical users, you may wish to hide this behind some more user-friendly config file. E.g.

# HIGH, MEDIUM, LOW - please change as appropriate. The guidelines are:
#                       * HIGH - users who generate 500 items per day
#                       * MEDIUM - 200-500 items etc

or possibly deploy different config files depending on which product option a user specifies when they order the product in the first place.

share|improve this answer
We added a command line option to our tool to basically pass VM size options through. Most of the customers are clueless about this; they expect their application to work without them getting involved, so this simply results in support phone calls. –  Ira Baxter Jul 20 '09 at 9:28
I don't think it unreasonable for a customer to have to indicate in some way what sort of usage pattern they have to expect: I've modified the answer –  oxbow_lakes Jul 20 '09 at 12:27
@oxbox: You may not think it unreasonable for customer to do this. But the customers do. And if you force them to guess, and they guess wrong, which they will, they just blame you for having a user-hostile tool. I already have this problem. –  Ira Baxter Aug 10 '10 at 14:18

In comments you say that the amount of memory that your application actually depends on the input dataset size provided by the user. This suggests that instead of trying to grab all available virtual memory (which may cause problems for the user's other applications) you should be looking at the input dataset size before you start the JVM and using that to estimate the amount of memory the application will need.

Suppose that the user's machine is configured with modest physical memory and a huge swap space. If you launch the JVM with a huge VM size, it could cause severe "thrashing" as the JVM tries to access data in non-resident pages. By contrast, if you give the JVM something more than the application needs and less than the available physical memory, you should be able to run comfortably without thrashing.

share|improve this answer
That wasn't what I proposed. What I want is something that allocates a pretty small first amount of VM, and then grew it each time the garbage collector finished with only a small amount of free space (say, 5%). Then VM demand stays small with small data, and grows to handle big data as required. In fact, AFAIK, it already does this. The failure is that I can't specify the upper bound in a target-machine independent way. –  Ira Baxter Jul 21 '09 at 8:47
I doubt that for any interesting application that it is easy to make an estimate of VM size vs. input size that works reliably. This is the big Oh problem; you might get the O(formula) sort of right, but you need that tuning constant, and if nothing else, different JVMs will have different constants, and different users will have different JVMs. The demand-memory-as-needed scheme is far safer –  Ira Baxter Jul 21 '09 at 8:50

I read through the threads but didn't see anything which indicated that the application had undergone some sort of profiling. Normally I'd profile the apps under certain conditions to find hot spots in performance or memory usage. There's probably things that could be improved in most cases.

If you could establish the limits and understand the behavior of the application you could be in a position to better tell your customers what they can or cannot do with the application thereby reducing the amount of support calls and giving you a better idea of what minimum or maximum heap size to ship the product with.

Maybe you could start with this: http://www.eclipse.org/mat/

share|improve this answer
Thanks for responding; not much traffic on this question recently. ... While in general I'd agree that (memory) profiling might be useful to ensure that memory is not wasted, we feel this application is pretty well engineered. The real problem is that the amount of data it must read and process for a single user can very literally by 2-3 orders of magnitude with a relatively heavy bias towards smaller sizes. This means the app works well with a modest allocation most of the time, and inevitably runs out of memory when that modest allocation meets the large input... –  Ira Baxter Jan 8 '10 at 15:14
... and because it works well with small, when the out-of-memory problem occurs, the users are so used to it running well that they never bother to learn about allocating stuff themselves. And frankly, while we can (and do) ask them to do this reallocation themselves, the entire problem is an artifact of the dumb way that the VM allocator works. If it simply took more memory when needed, there wouldn't be a problem. So, our users are a victim of a dumb design decision on the part of the JVM implementation, and of course we get take support calls as a consequence. –  Ira Baxter Jan 8 '10 at 15:16
Sounds like you've hit on a hot spot here (the reading part) to me :D. Ask the developers how they are doing the reading and data binding :P. –  pengtuck Jan 21 '10 at 4:34

Have you looked at running jps to give you the PID for your process and then calling jinfo to change the mx option? Not sure if this will work but it may.

[Edit] This would mean that when you think you have a big dataset, you read the total amount of ram somehow (OS dependent I think. See http://forums.sun.com/thread.jspa?messageID=10306570) or you just increase the size until you don't think it is low anymore (if it blows up first, try to capture and display a helpful message such as "your machine is inadequate, time to make a run to Frys").

share|improve this answer

This discussion is moot if you think that your clients can ask for 2-3GB of RAM on their 32-bit machine. The OS and other apps will be taking their pound of flesh to run as well.

Sounds like your app is reaching the point where it needs a 64-bit operating system and lots more RAM.

share|improve this answer
No. Some users (in fact most) only process small datasets, and are fine with smaller machines. Some have huge datasets. I can't insist they they all have 64 bit systems, and that seems unreasonable if they have only small data. –  Ira Baxter Jul 20 '09 at 9:45
PS: Going 64 bits doesn't change the dumb behavior of the -Xmx option. The JVM still chokes at startup if you don't have a number consistent with the specific machine VM configuration. –  Ira Baxter Jul 20 '09 at 10:10

Your Answer


