How to specify JVM argument for Maven built executable JAR

旧巷老猫 提交于 2019-12-19 17:14:24

问题


When using Maven to build an executable JAR, how do I specify the JVM arguments that are used when the JAR is executed?

I can specify the main class using <mainClass>. I suspect there's a similar attribute for JVM arguments. Specially I need to specify the maximum memory (example -Xmx500m).

Here's my assembly plugin:

  <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
      <archive>
        <manifest>
          <addClasspath>true</addClasspath>
          <mainClass>com.me.myApplication</mainClass>
        </manifest>
      </archive>
    </configuration>
  </plugin>

Edit/Follow-up: It seems that it might not be possible to specify JVM arguments for an executable JAR according to this and this post.


回答1:


I don't know of any such mechanism. The JVM configuration is specified by the calling java command.

Here's the jar file specification which conspicuously doesn't mention any attribute other than Main-Class for stand-alone execution:

http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html




回答2:


First off, let me say that anything this tricky is probably hard for a reason.

This approach that may work for you if you really need it. As written, it assumes "java" is on the caller's path.

Overview:

  1. Declare a Bootstrapper class as the main class in the jar's manifest.

  2. The bootstrapper spawns another process in which we call java (passing in any command-line arguments you want) on the "real" main class.

  3. Redirect the child processes System.out and System.err to the bootstrapper's respective streams

  4. Wait for the child process to finish

Here's a good background article.

src/main/java/scratch/Bootstrap.java - this class is defined in pom.xml as the jar's mainclass: <mainClass>scratch.Bootstrap</mainClass>

package scratch;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class Bootstrap {
    class StreamProxy extends Thread {
        final InputStream is;
        final PrintStream os;

        StreamProxy(InputStream is, PrintStream os) {
            this.is = is;
            this.os = os;
        }

        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line = null;
                while ((line = br.readLine()) != null) {
                    os.println(line);
                }
            } catch (IOException ex) {
                throw new RuntimeException(ex.getMessage(), ex);
            }
        }
    }

    private void go(){
        try {
            /*
             * Spin up a separate java process calling a non-default Main class in your Jar.  
             */
            Process process = Runtime.getRuntime().exec("java -cp scratch-1.0-SNAPSHOT-jar-with-dependencies.jar -Xmx500m scratch.App");

            /*
             * Proxy the System.out and System.err from the spawned process back to the user's window.  This
             * is important or the spawned process could block.
             */
            StreamProxy errorStreamProxy = new StreamProxy(process.getErrorStream(), System.err);
            StreamProxy outStreamProxy = new StreamProxy(process.getInputStream(), System.out);

            errorStreamProxy.start();
            outStreamProxy.start();

            System.out.println("Exit:" + process.waitFor());
        } catch (Exception ex) {
            System.out.println("There was a problem execting the program.  Details:");
            ex.printStackTrace(System.err);

            if(null != process){
                try{
                    process.destroy();
                } catch (Exception e){
                    System.err.println("Error destroying process: "+e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) {
        new Bootstrap().go();
    }

}

src/main/java/scratch/App.java - this is the normal entry point for your program

package scratch;

public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World! maxMemory:"+Runtime.getRuntime().maxMemory() );
    }
}

Calling: java -jar scratch-1.0-SNAPSHOT-jar-with-dependencies.jar Returns:

Hello World! maxMemory:520290304
Exit:0



回答3:


In response to David Carlson's answer, you can make it less brittle by using the java.home system property to locate the java executable instead of relying on the user's path to find it. In addition you should probably be redirecting standard input to the child process as well.




回答4:


I think this can be done if you think of it this way. Generate a .bat file that will have a command:

> java .. yourClass.. -D<jvmOption1> -D<jvmOption2>...

You can try looking on this app assembler plugin for maven.

I tried it, and seems to work. I am still not clear how to make .bat file to be generated with the somewhat different content, but I think it is doable.

As another option, you may always try to create the .bat file in the resource sub folder of your project and include that sub folder with your distribution.




回答5:


Ancient question but came up on my Google search for this exact problem so I'm answering it.

Try

<configuation>
...
<argLine> -Xmx500m </argLine>
...
</configuation>


来源:https://stackoverflow.com/questions/193483/how-to-specify-jvm-argument-for-maven-built-executable-jar

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!