Подтвердить что ты не робот

Получать JVM для увеличения потребности в памяти по мере необходимости до размера предела VM?

Мы отправляем приложение Java, чья потребность в памяти может сильно варьироваться в зависимости от размера обрабатываемых данных. Если вы не установите максимальный размер виртуальной памяти (VM), довольно часто JVM завершает работу с сбоем GC на больших данных.

Мы хотели бы видеть, что JVM запрашивает больше памяти, поскольку GC не может обеспечить достаточно, пока общая доступная VM не будет исчерпана. например, начать с 128 Мб и увеличивать геометрически (или какой-либо другой шаг) всякий раз, когда GC не удалось.

Командная строка JVM ( "Java" ) позволяет явно устанавливать максимальные размеры VM (различные команды -Xm *), и вы думаете, что это будет спроектировано так, чтобы быть адекватным. Мы пытаемся сделать это в файле .cmd, который мы отправляем с приложением. Но если вы выберете какой-то конкретный номер, вы получаете одно из двух плохих поведений: 1) если ваш номер достаточно мал, чтобы работать на большинстве целевых систем (например, 1 Гбит), он недостаточно велик для больших данных или 2) если вы сделаете его очень большим, JVM откажется работать в тех системах, фактическая VM которых меньше указанных.

Как настроить Java для использования доступной виртуальной машины при необходимости, не зная этого числа заранее и не захватывая все это при запуске?

4b9b3361

Ответ 1

Вы также можете использовать опцию: -XX: + AggressiveHeap

Это в соответствии с [документацией] [1]:

Параметр -XX: + AggressiveHeap проверяет ресурсы машины (размер памяти и количества процессоров) и пытается установить различные параметры быть оптимальным для долговременной памяти требующие выделения ресурсов. это было первоначально предназначенный для машин с большой объем памяти и большой количество процессоров, но в J2SE платформа, версия 1.4.1 и более поздняя версия показала себя полезной даже на четыре процессора. С этим опция сборщик пропускной способности (-XX: + UseParallelGC) используется вдоль с адаптивной калибровкой (-XX: + UseAdaptiveSizePolicy). физическая память на машинах должна быть не менее 256 МБ до Можно использовать AggressiveHeap. Размер начальной кучи вычисляется основанный на размере физического памяти и попыток сделать максимальную использование физической памяти для куча (т.е. алгоритмы пытаются используйте кучи, размером с физической памяти).

[1]: http://java.sun.com/docs/hotspot/gc1.4.2/#4.2.2. AggressiveHeap | контур

Ответ 2

Максимальные размеры VM действительно отвечают на эту потребность (он устанавливает максимальное значение, но виртуальная машина будет принимать только необходимые, шаг за шагом), но если вам нужно несколько конфигураций, помимо предоставления разных файлов "cmd", я действительно вижу способ (хотя я буду искать немного больше)

[править] Как насчет использования первой программы /script (или даже другой java-программы), которая проверяет доступные ресурсы для системы, а затем вызывается только ваша программа с соответствующим -Xm, в зависимости от того, что она извлекает из системы? Таким образом, он будет адаптироваться к машинам, даже если вы не знаете их раньше. Может быть идея...

[второе редактирование] Хорошо, это уже было предложено skaffman, мое плохое.

Ответ 3

У нас есть небольшое приложение C, которое мы используем для запуска всех наших приложений Java через JNI. Это позволяет нам:

  • Иметь значащее имя процесса (esp important под окнами)
  • У нас есть свои собственные значки (опять же важные для Windows)
  • Динамически создавать classpaths (мы анализируем содержимое файла /lib для автоматического включения всех банок)

Для наших приложений мы просто жестко определяем ограничение кучи, но вы можете легко динамически настраивать максимальный размер кучи на основе доступной памяти.

Такое небольшое приложение на самом деле довольно легко сделать (это одна из самых простых вещей, связанных с JNI). Хорошей отправной точкой стал бы источник JDK (там есть подпапка для самого java.exe, которую вы можете использовать, - то, что мы сделали). Большинство людей с удивлением обнаруживают, что java.exe - небольшое крошечное приложение (< 200 строк кода), которое просто вызывает JNI и передает аргументы командной строки (heck, даже использование метода main() довольно необязательно как только вы начнете запускать вещи сами).

Здесь код, который не только запускает JVM и т.д., но также определяет максимальное пространство кучи на основе доступной ОЗУ компьютера. Это много кода для SO-сообщения, и это совсем не симпатично - но это боевой код - он использовался почти десять лет на протяжении многих сотен инсталляций и т.д. Наслаждайтесь:

#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;
    else 
        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 
        jenv->ExceptionClear();

        jmethodID gmID = jenv->GetMethodID( 
                           jenv->FindClass("java/lang/Throwable"),
                           "getMessage",
                           "()Ljava/lang/String;");


        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());
            argStart++;
        }
    }

    // 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());
        fclose(f);

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

    }

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

    vAddOption(sOption_HeapSpace);
    vAddOption(sOption_PermSize);
    vAddOption(sOption_HeapDump);
    vAddOption(sOption_JavaLibraryPath);

    // 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);

    SetCurrentDirectory(sBinPath.c_str());

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

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

    SetCurrentDirectory(originalCurrentDirectory);
    if (lpfnSetDllDirectory){
        lpfnSetDllDirectory(NULL);
    }

    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";

        vShowJREError(ss.str());
        // 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)){
        vShowJREError(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";
        vShowJREError(sErrorMessage);
        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 + "]";
        vShowError(sErrorMessage);
        vDestroyVM(env, jvm);
        return 0;
    }


    // find startup method
    string sStartupMethod_Identifier = "main";
    string sStartupMethod_TypeDescriptor =
    "([Ljava/lang/String;)V";
    midStartup = 
    env->GetStaticMethodID(jclassStartup,
    sStartupMethod_Identifier.c_str(),
    sStartupMethod_TypeDescriptor.c_str());
    if (midStartup == NULL) {
        string sErrorMessage =
            "Unable to find startup method ["
            + sStartupClass + "."
            + sStartupMethod_Identifier
            + "] with type descriptor [" +
            sStartupMethod_TypeDescriptor + "]";
        vShowError(sErrorMessage);
        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)){
        vShowError(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
    jvm->DestroyJavaVM();


    return 0;

}


void vDestroyVM(JNIEnv *env, JavaVM *jvm)
{
    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
    }
    jvm->DestroyJavaVM();
}


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

void vShowJREError(string sError) {
    MessageBox(NULL, sError.c_str(), "Startup Error", MB_OK);
    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;
    FormatMessage(  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    GetLastError(),
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    (LPTSTR) &lpSystemMsgBuf, 0, NULL );
    string sSystemError = string((LPTSTR)lpSystemMsgBuf);
    vShowError(sLocalError + " [" + sSystemError + "]");
}


void vAddOption(string& sValue) {
    mctOptions++;
    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));
            free(vm_options);
            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)){
            FindClose(find);
            find = NULL;
        }
    }

    return cp;
}

string GetJREPath(){

    // first, check for JRE in application directory
    char home[2000];
    if (!GetApplicationHome(home, sizeof(home))) {
        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;
            }

            free(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;
                        }

                        free(pData);
                    }
                    RegCloseKey(hKeyJREInstance);
                }
            }
        }
        RegCloseKey(hKeyJRERoot);
    }

    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;

    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;
}

Ответ 4

Еще один вариант... Я работаю над пусковой установкой WinRun4J, что позволяет указать максимальный размер кучи в процентах от доступной памяти на компьютере, на котором он работает (т.е. проверяет объем доступной памяти и динамически устанавливает параметр -Xmx при запуске).

Параметр INI - "vm.heapsize.max.percent". Существует также еще одна опция "vm.heapsize.preferred", которая устанавливает параметр -Xmx как максимальную доступную память на машине до этой суммы.

Я считаю, что некоторые другие пусковые установки (например, Launch4J, Janel) предлагают ту же функциональность.

Ответ 5

Я думаю, вам не повезло:-( Параметры -Xms и -Xmx не обеспечивают эту гибкость.

Итак, я думаю, вам нужно будет связать ваш JVM-вызов с script, который может определить максимальный объем памяти, а затем соответствующим образом установить -Xmx (возможно,.vbs script с помощью WMI в Windows). Или, возможно, он спрашивает пользователей при первом запуске?

Немного боль, я боюсь.

Ответ 6

Я думаю, что самый простой способ сделать это - запустить JVM через некоторое приложение-оболочку, которое будет проверять системные ресурсы для определения доступности памяти, а затем запустить JVM с соответствующим параметром -Xmx.

Затем возникает вопрос, как будет выглядеть эта оболочка. Возможно даже, что приложение-оболочка может быть JVM, хотя я не думаю, что API или системные свойства предоставят необходимую информацию. Возможно, оболочка script или ваш выбор может получить информацию.

Ответ 7

если у вас есть много времени на руку, вы можете попробовать следующее:

Попытайтесь получить то, что является необходимым объемом памяти и набором данных ввода. С помощью этого вы можете разделить обработку в другом наборе классов и создать новый процесс JVM для фактической обработки данных. В основном менеджер и рабочий. Менеджер проведет базовый анализ требуемого набора данных и создаст Работника с соответствующими требованиями к памяти. Вероятно, вы также можете настроить диспетчера на знание среды и предупредить пользователя, когда они пытаются работать с набором данных, который не может обрабатывать их машина.

Это в значительной степени расширение ответа, предоставленного skaffman, но будет происходить в рамках одного и того же приложения, насколько это касается пользователя.

Ответ 8

В аргументах виртуальной машины могут использоваться два параметра: -Xms для установки размера памяти при запуске и -Xmx для установки максимального объема памяти...

Вы можете установить низкую начальную память и большую максимальную, поэтому VM будет выделять новую память только при необходимости.

Ответ 9

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

Конечно, если ваш продукт нацелен на очень нетехнических пользователей, вы можете скрыть это за еще более удобным конфигурационным файлом. Например.

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

или, возможно, развернуть разные файлы конфигурации в зависимости от того, какой параметр продукта пользователь указывает, когда он заказывает продукт в первую очередь.

Ответ 10

Я не думаю, что Sun или IBM JVM могут это сделать (я знаю, что AS/400 можно, но это, скорее всего, не относится к вам).

Я бы предложил использовать Java WebStart (и прежде чем отменить это, а затем обратите внимание, что он был обновлен с помощью Java 6 u 10 и намного лучше подходит для запуска "локальных" приложений и апплетов), поскольку он позволяет вам предоставить "" маленький экземпляр "," большой экземпляр "," гигантский экземпляр" в качестве ссылок/значков.

Вы, скорее всего, зайдете в опции "внедрить приложение в кеш-кеш" и "офлайн".

Ответ 11

В комментариях вы говорите, что объем памяти, который ваше приложение действительно зависит от размера входного набора данных, предоставленного пользователем. Это говорит о том, что вместо того, чтобы пытаться захватить всю доступную виртуальную память (что может вызвать проблемы для других приложений пользователя), вы должны посмотреть размер входного набора данных, прежде чем запускать JVM, и использовать это для оценки объема памяти, в которой будет нуждаться приложение.

Предположим, что пользовательский компьютер настроен на скромную физическую память и огромное пространство подкачки. Если вы запускаете JVM с огромным размером виртуальной машины, это может привести к серьезному "перебиванию", поскольку JVM пытается получить доступ к данным на нерезидентных страницах. В отличие от этого, если вы даете JVM нечто большее, чем приложение, и меньше, чем доступная физическая память, вы должны иметь возможность работать без перерыва.

Ответ 12

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

Если вы можете установить ограничения и понять поведение приложения, вы можете быть в состоянии лучше сообщить своим клиентам, что они могут или не могут сделать с приложением, тем самым уменьшив количество вызовов поддержки и давая вам лучшее представление о какой минимальный или максимальный размер кучи для отправки продукта.

Возможно, вы могли бы начать с этого: http://www.eclipse.org/mat/

Ответ 13

Посмотрели ли вы на запуск jps, чтобы дать вам PID для вашего процесса, а затем вызвать jinfo для изменения опции mx? Не уверен, что это сработает, но может.

[Edit] Это будет означать, что, когда вы думаете, что у вас большой набор данных, вы как-то читаете общий объем памяти (зависит от ОС. См. http://forums.sun.com/thread.jspa?messageID=10306570), или просто увеличите размер, пока вы не подумаете, что он слишком низко (если он сначала взрывается, попытайтесь захватить и отобразить полезное сообщение, такое как "ваша машина неадекватна, время, чтобы сделать бег к Frys" ).

Ответ 14

Это обсуждение является спорным, если вы считаете, что ваши клиенты могут обратиться за 2-3 Гбайт оперативной памяти на их 32-битной машине. ОС и другие приложения будут использовать свой фунт плоти для запуска.

Похоже, ваше приложение достигает точки, где ему нужна 64-разрядная операционная система и много больше оперативной памяти.