Получить JVM для увеличения потребности в памяти по мере необходимости до предела размера виртуальной машины? - PullRequest
16 голосов
/ 20 июля 2009

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

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

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

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

Ответы [ 14 ]

13 голосов
/ 20 июля 2009

Вы также можете использовать опцию: -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 | контур

8 голосов
/ 21 июля 2009

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

  1. Имеет осмысленное имя процесса (особенно в Windows)
  2. Иметь собственные значки (опять же, важно для Windows)
  3. Динамически создавать пути к классам (мы разбираем содержимое файла / lib для автоматического включения всех jar-файлов)

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

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

Вот код, который не только запускает 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;
}
8 голосов
/ 20 июля 2009

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

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

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

7 голосов
/ 21 июля 2009

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

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

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

5 голосов
/ 20 июля 2009

Я думаю, вам не повезло :-( Опции -Xms и -Xmx не обеспечивают такой гибкости.

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

Боюсь, немного боли.

4 голосов
/ 20 июля 2009

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

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

1 голос
/ 20 июля 2009

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

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

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

0 голосов
/ 26 марта 2010

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

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

0 голосов
/ 08 января 2010

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

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

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

0 голосов
/ 21 июля 2009

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

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

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