java 如何为 Maven 构建的可执行 JAR 指定 JVM 参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/193483/
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 11:21:53  来源:igfitidea点击:

How to specify JVM argument for Maven built executable JAR

javamaven-2jarexecutable-jar

提问by Steve Kuo

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

使用 Maven 构建可执行 JAR 时,如何指定执行 JAR 时使用的 JVM 参数?

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

我可以使用<mainClass>. 我怀疑 JVM 参数有一个类似的属性。特别是我需要指定最大内存(例如 -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 thisand thispost.

编辑/跟进:根据这篇这篇文章,似乎不可能为可执行 JAR 指定 JVM 参数。

采纳答案by Alex Miller

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

我不知道有任何这样的机制。JVM 配置由调用 java 命令指定。

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

这是 jar 文件规范,它明显没有提及除 Main-Class 之外的任何用于独立执行的属性:

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

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

回答by David Carlson

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.

如果您真的需要,这种方法可能对您有用。正如所写,它假定“java”在调用者的路径上。

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

  1. 将 Bootstrapper 类声明为 jar 清单中的主类。

  2. 引导程序产生另一个进程,在该进程中,我们在“真正的”主类上调用 java(传入您想要的任何命令行参数)。

  3. 将子进程 System.out 和 System.err 重定向到引导程序各自的流

  4. 等待子进程完成

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>

src/main/java/scratch/Bootstrap.java- 这个类在 pom.xml 中定义为 jar 的主类:<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

src/main/java/scratch/App.java- 这是程序的正常入口点

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

调用:java -jar scratch-1.0-SNAPSHOT-jar-with-dependencies.jar返回:

Hello World! maxMemory:520290304
Exit:0

回答by jgibson

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.

为了回应 David Carlson 的回答,您可以通过使用 java.home 系统属性来定位 java 可执行文件而不是依赖用户的路径来查找它,从而使其不那么脆弱。此外,您可能还应该将标准输入重定向到子进程。

回答by Dmitriy Ryabin

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

如果你这样想的话,我认为这是可以做到的。生成一个.bat包含命令的文件:

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

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

You can try looking on this app assembler pluginfor maven.

您可以尝试查看这个适用于 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.

我试过了,似乎有效。我仍然不清楚如何使用有些不同的内容生成 .bat 文件,但我认为这是可行的。

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.

作为另一种选择,您可以始终尝试在项目的资源子文件夹中创建 .bat 文件,并将该子文件夹包含在您的发行版中。

回答by Leonard Hagger

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

古老的问题,但在我的谷歌搜索中出现了这个确切的问题,所以我正在回答它。

Try

尝试

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