java 让 JVM 根据需要将内存需求增加到 VM 限制的大小?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1152507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:23:19  来源:igfitidea点击:

Get JVM to grow memory demand as needed up to size of VM limit?

javacommand-linevirtual-memory

提问by Ira Baxter

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.

我们发布了一个 Java 应用程序,其内存需求可能会因所处理数据的大小而有很大差异。如果您没有设置最大 VM(虚拟内存)大小,JVM 通常会因大数据的 GC 失败而退出。

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.

我们希望看到的是 JVM 请求更多内存,因为 GC 无法提供足够的内存,直到总可用 VM 耗尽。例如,从 128Mb 开始,并在 GC 失败时以几何级数(或某些其他步骤)增加。

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.

JVM(“Java”)命令行允许显式设置最大 VM 大小(各种 -Xm* 命令),您会认为这样设计就足够了。我们尝试在随应用程序一起提供的 .cmd 文件中执行此操作。但是,如果您选择任何特定数字,则会出现以下两种不良行为之一:1) 如果您的数字小到可以在大多数目标系统上运行(例如 1Gb),则它不足以容纳大数据,或者 2) 如果你让它变得非常大,JVM 拒绝在那些实际 VM 小于指定的系统上运行。

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?

如何设置 Java 以在需要时使用可用的 VM,而无需事先知道该数字,也无需在启动时全部获取?

回答by Sindri Traustason

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

您还可以使用选项:-XX:+AggressiveHeap

This according to the [documentation][1]:

这根据[文档][1]:

The -XX:+AggressiveHeap option inspects the machine resources (size of memory and number of processors) and attempts to set various parameters to be optimal for long-running, memory allocation-intensive jobs. It was originally intended for machines with large amounts of memory and a large number of CPUs, but in the J2SE platform, version 1.4.1 and later it has shown itself to be useful even on four processor machines. With this option the throughput collector (-XX:+UseParallelGC) is used along with adaptive sizing (-XX:+UseAdaptiveSizePolicy). The physical memory on the machines must be at least 256MB before AggressiveHeap can be used. The size of the initial heap is calculated based on the size of the physical memory and attempts to make maximal use of the physical memory for the heap (i.e., the algorithms attempt to use heaps nearly as large as the total physical memory).

-XX:+AggressiveHeap 选项检查机器资源(内存大小和处理器数量)并尝试设置各种参数以优化长时间运行的内存分配密集型作业。它最初是为具有大量内存和大量 CPU 的机器设计的,但在 J2SE 平台 1.4.1 及更高版本中,它甚至在四个处理器的机器上也显示出自己的用处。使用此选项,吞吐量收集器 (-XX:+UseParallelGC) 与自适应大小 (-XX:+UseAdaptiveSizePolicy) 一起使用。在使用 AggressiveHeap 之前,机器上的物理内存必须至少为 256MB。初始堆的大小是根据物理内存的大小计算的,并试图最大限度地利用物理内存为堆(即,

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

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

回答by Kevin Day

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

我们有一个小型 C 应用程序,用于通过 JNI 启动所有 Java 应用程序。这使我们能够:

  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)
  1. 有一个有意义的进程名(尤其是windows下很重要)
  2. 拥有我们自己的图标(同样,对于 Windows 很重要)
  3. 动态构建类路径(我们解析 /lib 文件的内容以自动包含所有 jar)

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).

这种小应用程序实际上很容易做(这是用 JNI 做的最简单的事情之一)。一个好的起点是 JDK 的源代码(您可以使用 java.exe 本身的一个子文件夹 - 这就是我们所做的)。大多数人都非常惊讶地发现 java.exe 是一个很小的应用程序(< 200 行代码),它只调用 JNI 并传入命令行参数(见鬼,即使使用名为 main() 的方法也是非常可选的一次你开始自己启动东西)。

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 :

这里的代码不仅可以启动 JVM 等,还可以根据计算机的可用 RAM 确定最大堆空间。这是一篇 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, '\') = '
# 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
'; if ((cp = strrchr(buf, '\')) == 0) { // This happens if the application is in a // drive root, and there is no bin directory. buf[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; }

回答by Gnoupi

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)

最大 VM 大小确实满足了这一需求(它设置了最大值,但 VM 只会一步一步地进行必要的操作),但是如果您需要多个配置,除了提供不同的“cmd”文件之外,我真的看不到一种方式(虽然我会搜索更多)

[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...

[编辑] 如何使用第一个程序/脚本(甚至另一个 java 程序),它会检查系统的可用资源,然后根据它从系统检索到的内容,仅使用适当的 -Xm 调用您的程序?这样它就会适应机器,即使你以前不知道它们。可能是一个想法...

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

[第二次编辑] 好的,这已经由skaffman提出了,我的错。

回答by Peter Smith

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).

还有一个选项......我在一个名为WinRun4J的启动器上工作,它允许您将最大堆大小指定为运行它的机器上可用内存的百分比(即它检查可用内存量和在启动时动态设置 -Xmx 参数)。

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.

INI 选项是“vm.heapsize.max.percent”。还有另一个选项“vm.heapsize.preferred”,它将 -Xmx 参数设置为机器上最大可用内存,最多可达此数量。

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

我相信其他一些启动器(例如 Launch4J、Janel)提供了相同的功能。

回答by Brian Agnew

I think you're out of luck :-( The -Xmsand -Xmxoptions don't provide that flexibility.

我认为你运气不好:-(-Xms-Xmx选项不提供这种灵活性。

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 -Xmxappropriately (probably a .vbs script using WMIon Windows). Or perhaps it asks the users the first time it's run ?

因此,我认为您需要使用可以确定最大内存量的脚本来包装 JVM 调用,然后进行-Xmx适当的设置(可能是在 Windows 上使用WMI的 .vbs 脚本)。或者它在第一次运行时询问用户?

A bit of a pain, I fear.

有点痛,我害怕。

回答by skaffman

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.

我认为最简单的方法是通过一些包装应用程序启动 JVM,这将检查系统资源以确定内存可用性,然后使用适当的 -Xmx 参数启动 JVM。

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.

那么问题就变成了如何编写该包装器。包装器应用程序本身甚至可能是一个 JVM,尽管我认为 API 或系统属性不会公开必要的信息。也许 shell 脚本或您的选择可以获取信息。

回答by Newtopian

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.

尝试获得所需的内存与输入数据集。有了这个,您可以将处理拆分为一组不同的类,并创建一个新的 JVM 进程来实际处理数据。基本上是经理和工人。Manager 将对所需的数据集进行基本分析,并生成具有适当内存要求的 Worker。您可能还可以将您的 Manager 设置为了解环境,并在用户尝试操作他们的机器无法处理的数据集时警告用户。

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.

这几乎是 skaffman 提供的答案的扩展,但就用户而言,这一切都将发生在同一个应用程序中。

回答by Thorbj?rn Ravn Andersen

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).

我不认为 Sun 或 IBM JVM 可以做到这一点(我知道 AS/400 可以,但这很可能与您无关)。

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.

我建议使用 Java WebStart(在你放弃它之前,请注意它已经用 Java 6 u 10 更新并且更适合启动“本地”应用程序和小程序),因为它允许你提供一个“小实例” ,“更大的实例”,“巨大的实例”作为链接/图标。

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

您很可能会查看“在 webstart 缓存中注入应用程序”和“离线”选项。

回答by oxbow_lakes

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

我不认为你可以做你想做的事;相反,你将不得不出货指令具体到你的客户,他们的系统和他们如何才能够修改您的需求.cmd文件,以允许更多的内存。

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.

当然,如果您的产品是针对非常非技术的用户,您可能希望将其隐藏在一些对用户更友好的配置文件后面。例如

##代码##

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

或者可能根据用户在订购产品时指定的产品选项部署不同的配置文件。

回答by Vinze

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...

可以使用虚拟机参数中有两个选项:-Xms 设置启动时的内存大小和 -Xmx 设置最大内存大小...

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

你可以设置一个低的启动内存和一个大的最大值,这样虚拟机只会在需要时分配新内存。