※当サイトの記事には、広告・プロモーションが含まれます。

JavaのJVMのオプションの設定が分かり辛いが、明示的に設定しないとデフォルト値が使われる模様

www.itmedia.co.jp

⇧ X(旧:Twitter)で話題になってたけど、研究が続いていたんですな、餃子が食べたくなりますな。

JavaJVMのオプションの設定が分かり辛いが、明示的に設定しないとデフォルト値が使われる模様

Javaのアプリケーションを起動する際に、「Java仮想マシンJVMJava Virtual Machine )」のオプションを設定することができるのですが、設定した「Java仮想マシンJVMJava Virtual Machine )」のオプションの値をリセットするにはどうしたら良いのかを知りたかったんだけど、見事にドキュメントに記載が無い...

流石は、Oracleさん、安定の不親切さ...

で、致し方ないから、実際に動かしてみて検証してみたところ、「Java仮想マシンJVMJava Virtual Machine )」が終了した段階で、設定した「Java仮想マシンJVMJava Virtual Machine )」のオプションの値も棄却されるらしい。

つまり、「Java仮想マシンJVMJava Virtual Machine )」のインスタンス毎にオプションの設定がなされるのだと。

なので、アプリケーションサーバーなんかで稼働してるJavaアプリケーションに紐づく「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定を変更したい場合は、再起動が必要ということらしい。(「Java仮想マシンJVMJava Virtual Machine )」のインスタンスを生成し直すってことになるんかな?)

で、例の如く、

ts0818.hatenablog.com

⇧ 上記の記事の時の環境で、試してみました。

■/home/ts0818/work/test_jvm_option/execute_jstat.sh

#!/bin/bash

## ■■■■■■■■■■■■■■■
## ■ 変数定義
## ■■■■■■■■■■■■■■■
jstat_TestJvmOption_tsv="jstat.tsv"
#jvm_start_option_Xmx1024m="-Xmx1024m"
jvm_start_option_Xmx2048m="-Xmx2048m"
jvm_start_option_PrintFlagsFinal="-XX:+PrintFlagsFinal"
interval_millsecond=100
limit_display_result_row_count=20000
datetime_format="%Y/%m/%d %H:%M:%S.%3N"
jvm_java_version="-version"
result_log="result.log"

## ■■■■■■■■■■■■■■■
## ■ ファイル初期化
## ■■■■■■■■■■■■■■■
if [ -e ${jstat_TestJvmOption_tsv} ]; then
  rm ${jstat_TestJvmOption_tsv} && touch ${jstat_TestJvmOption_tsv}
else
  touch ${jstat_TestJvmOption_tsv}
fi

## ■■■■■■■■■■■■■■■
## ■ Java実行
## ■■■■■■■■■■■■■■■
# JVMのバージョンを確認
#execute_java_version=$(java ${jvm_java_version})
java ${jvm_java_version} 2>> ${result_log} &&

# JVM起動時の設定を確認(Xmxの設定のみ表示)
jvm_setting_output=$(java ${jvm_start_option_Xmx2048m} ${jvm_start_option_PrintFlagsFinal} 2>&1 | grep -Ei "MaxHeapSize")
echo "${jvm_setting_output}" >> ${result_log} &&

# TestJvmOptionクラスの実行
class_TestJvmOption="TestJvmOption"
jvm_start_option_flight_recorder="-XX:StartFlightRecording=dumponexit=true,filename="
output_jfr_TestJvmOption=TestJvmOption_flight_recording_$(date +"%Y-%m-%d_%H-%M-%S").jfr

#java ${jvm_start_option_Xmx2048m} ${class_TestJvmOption} $1 >> ${result_log} & 
java ${jvm_start_option_flight_recorder}${output_jfr_TestJvmOption},name=${class_TestJvmOption} ${jvm_start_option_Xmx2048m} ${class_TestJvmOption} $1 >> ${result_log} & 
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gcutil {} ${interval_millsecond} ${limit_display_result_row_count} | awk -F, '{print strftime(`${datetime_format}`,systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | awk -F, '{print strftime("%(${datetime_format})",systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#jps -lm | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | awk -v datetime_format="${datetime_format}" -F, '{print strftime(datetime_format, systime()) $1}' >> ${jstat_TestJvmOption_tsv}

# heap dump fileを取得する
jps | grep TestJvmOption | awk '{print $1}' | xargs -I{} jmap -dump:format=b,file=./TestJvmOption_heapdump.hprof {} &

# jstatコマンドの出力をファイルに追記(ヘッダー行を含む)
#jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') ${interval_millsecond} ${limit_display_result_row_count} |
jps | grep TestJvmOption | awk '{print $1}' | xargs -I{} jstat -gc {} ${interval_millsecond} ${limit_display_result_row_count} | 
awk -v datetime_format="${datetime_format}" 'BEGIN { OFS="\t" } {
  split($1, arr, " ");
  ms = sprintf("%.3f", arr[1] / 1000); # ミリ秒を秒に変換し、小数点以下3桁までの文字列にフォーマット
  cmd = "date +\"" datetime_format "\""; # 日時フォーマットを含むdateコマンドを生成
  cmd | getline formatted_date; # dateコマンドを実行してフォーマットされた日時を取得
  close(cmd);

  # 各列をタブ区切りで結合
  output = formatted_date "." ms;
  for (i = 1; i <= NF; i++) {
    output = output "\t" $i;
  }
  print output;
}' >> ${jstat_TestJvmOption_tsv}

# -Xmx2048mのJVMオプションを設定して起動
jvm_option_status_with_setting=$(java ${jvm_start_option_Xmx2048m} ${jvm_start_option_PrintFlagsFinal} 2>&1)
echo "${jvm_option_status_with_setting}" > jvm_option_status_with_setting.log

# JVMオプションを何も設定せずに起動
jvm_option_status_without_setting=$(java ${jvm_start_option_PrintFlagsFinal} 2>&1)
echo "${jvm_option_status_without_setting}" > jvm_option_status_without_setting.log

#java ${jvm_start_option_PrintFlagsFinal} ${jvm_start_option_Xmx} TestJvmOption $1 >> result.log &
#while jps | grep -q TestJvmOption; 
#do 
#  jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 >> jstat.log; 
#done

#until jps | grep TestJvmOption
#  do 
#    #echo "start jstat TestJvmOption"
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 >> ${jstat_TestJvmOption_tsv}
#  done

#until jps | grep -q TestJvmOption
#  do 
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | awk -F, '{print strftime("%Y/%m/%d %H:%M:%S",systime()) $1}' >> ${jstat_TestJvmOption_tsv}
#  done

#until jps | grep -q TestJvmOption
#  do
#    jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | perl -wn -e '$d = `date "+%Y/%m/%d %H:%M:%S"`; chomp $d; print "$d | $_"' >> ${jstat_TestJvmOption_tsv}
#  done


#jstat -gc $(jps | grep TestJvmOption | awk '{print $1}') 100 20000 | awk -F, '{print strftime("%Y/%m/%d %H:%M:%S",systime()) $1}';

⇧ のシェルスクリプトのファイルを実行すると、

JVMオプションを設定した場合と、設定しなかった場合のログが出力されるので、中身を見てみると

Java仮想マシンJVMJava Virtual Machine )」のオプションの設定ありで起動のログ抜粋

[Global flags]
     intx ActiveProcessorCount                      = -1                                  {product}
    uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
    uintx AdaptiveSizePausePolicy                   = 0                                   {product}
...省略
     bool ManagementServer                          = false                               {product}
    uintx MarkStackSize                             = 4194304                             {product}
    uintx MarkStackSizeMax                          = 536870912                           {product}
    uintx MarkSweepAlwaysCompactCount               = 4                                   {product}
    uintx MarkSweepDeadRatio                        = 1                                   {product}
     intx MaxBCEAEstimateLevel                      = 5                                   {product}
     intx MaxBCEAEstimateSize                       = 150                                 {product}
    uintx MaxDirectMemorySize                       = 0                                   {product}
     bool MaxFDLimit                                = true                                {product}
    uintx MaxGCMinorPauseMillis                     = 18446744073709551615                    {product}
    uintx MaxGCPauseMillis                          = 18446744073709551615                    {product}
    uintx MaxHeapFreeRatio                          = 100                                 {manageable}
    uintx MaxHeapSize                              := 2147483648                          {product}
     intx MaxInlineLevel                            = 9                                   {product}
     intx MaxInlineSize                             = 35                                  {product}
...省略
     intx hashCode                                  = 5                                   {product}

Java仮想マシンJVMJava Virtual Machine )」のオプションの設定なしで起動のログ抜粋

[Global flags]
     intx ActiveProcessorCount                      = -1                                  {product}
    uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
    uintx AdaptiveSizePausePolicy                   = 0                                   {product}
...省略
     bool ManagementServer                          = false                               {product}
    uintx MarkStackSize                             = 4194304                             {product}
    uintx MarkStackSizeMax                          = 536870912                           {product}
    uintx MarkSweepAlwaysCompactCount               = 4                                   {product}
    uintx MarkSweepDeadRatio                        = 1                                   {product}
     intx MaxBCEAEstimateLevel                      = 5                                   {product}
     intx MaxBCEAEstimateSize                       = 150                                 {product}
    uintx MaxDirectMemorySize                       = 0                                   {product}
     bool MaxFDLimit                                = true                                {product}
    uintx MaxGCMinorPauseMillis                     = 18446744073709551615                    {product}
    uintx MaxGCPauseMillis                          = 18446744073709551615                    {product}
    uintx MaxHeapFreeRatio                          = 100                                 {manageable}
    uintx MaxHeapSize                              := 1027604480                          {product}
     intx MaxInlineLevel                            = 9                                   {product}
     intx MaxInlineSize                             = 35                                  {product}
...省略
     intx hashCode                                  = 5                                   {product}

⇧ と、「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定で、明示的に設定した項目(今回は、XX:Xmx)だけ異なっていることから、オプションの設定は明示的に設定しない場合は常にデフォルトの設定値が利用されてるようです。

docs.oracle.com

Server JVM Default Initial and Maximum Heap Sizes

The default initial and maximum heap sizes work similarly on the server JVM as it does on the client JVM, except that the default values can go higher. On 32-bit JVMs, the default maximum heap size can be up to 1 GB if there is 4 GB or more of physical memory. On 64-bit JVMs, the default maximum heap size can be up to 32 GB if there is 128 GB or more of physical memory. You can always set a higher or lower initial and maximum heap by specifying those values directly; see the next section.

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size

⇧「WSL 2(Windows Subsystem for Linux 2)」のUbuntuで稼働する場合の「Java仮想マシンJVMJava Virtual Machine )」がどういう扱いになるのかが分からんのだけど、一応、ホスト側(Windows 10 Home)のメモリは8GBなんだけど、そもそも64bitのJVMが128GB以上の説明しかないから、何とも言えんのだけど、自分の環境では、

⇧ 64bitの「Java仮想マシンJVMJava Virtual Machine )」ということらしいのだけど、

 1027604480 ÷ 1024 ÷ 1024 = 980

が、Xmxのデフォルト値らしいので、概ね、1GB程度ってことになるんかね、う~む、どういう計算式で算出されてるかが分からんが...

というわけで、「Java仮想マシンJVMJava Virtual Machine )」の設定は、「Java仮想マシンJVMJava Virtual Machine )」のインスタンス毎に有効になるようなので、Javaアプリケーションを稼働させる時に起動する「Java仮想マシンJVMJava Virtual Machine )」毎に考えれば良いようです。

つまり、何かしらデフォルト以外の設定にしたい場合は「Java仮想マシンJVMJava Virtual Machine )」を起動し直す際に、明示的に設定してあげる必要がありそうです。

逆に言うと、起動時にしか「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定は、設定できないということなんかね?

Java仮想マシンJVMJava Virtual Machine )」もオブジェクト指向になっているってことですかな。

イメージ的には、

⇧ みたいな感じで、javaコマンドの実行時にオプションの設定ありだった場合に、「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定のデフォルト値を上書きして「Java仮想マシンJVMJava Virtual Machine )」のインスタンスを生成・起動するってことなんでしょうね、たぶん。

ちなみに、ドキュメントを見た限りでは、「Java仮想マシンJVMJava Virtual Machine )」のオプションは、

Java 8

docs.oracle.com

Java 11

Overview of Java Options

The java command supports a wide range of options in the following categories:

  • Standard Options for Java: Options guaranteed to be supported by all implementations of the Java Virtual Machine (JVM). They’re used for common actions, such as checking the version of the JRE, setting the class path, enabling verbose output, and so on.

  • Extra Options for Java: General purpose options that are specific to the Java HotSpot Virtual Machine. They aren’t guaranteed to be supported by all JVM implementations, and are subject to change. These options start with -X.

The advanced options aren’t recommended for casual use. These are developer options used for tuning specific areas of the Java HotSpot Virtual Machine operation that often have specific system requirements and may require privileged access to system configuration parameters. Several examples of performance tuning are provided in Performance Tuning Examples. These options aren’t guaranteed to be supported by all JVM implementations and are subject to change. Advanced options start with -XX.

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

Javaのバージョンによって変わってくるってことなんかね?

Java 8 Java 11
Standard Options Standard Options for Java
Non-Standard Options Extra Options for Java
Advanced Runtime Options Advanced Runtime Options for Java
Advanced JIT Compiler Options Advanced JIT Compiler Options for java
Advanced Serviceability Options Advanced Serviceability Options for Java
Advanced Garbage Collection Options Advanced Garbage Collection Options for Java

Java 8とJava 11で比較してみたけども、概ね、同じ感じってことになるんかね?

一応、Java 17とJava 21のドキュメントも確認してみたけど、

Java 17

docs.oracle.com

Java 21

docs.oracle.com

Java 11と大枠は変わっていなさそう。

Javaのドキュメントに限らないけど、「MECE(Mutually Exclusive and Collectively Exhaustive)」な感じで整備して欲しいっすな。

たらい回しにする場合は、参照先のリンクを掲載して欲しいんよね...

あとは、内輪だけで通じるような用語を使わないで欲しいよね...

初見の人が把握しやすい内容でお願いしたいところです。

ちなみに、「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定のデフォルト値を恒久的に変更できるのかどうかは分かりませんでした...ドキュメントで明示して欲しいのは、そういうとこだと思うよ...

で、ネットの情報をググっていたら、

stackoverflow.com

⇧ stackoverflowによると、

What I Know: 

So far I have found out that:

  • JAVA_OPTS is not used by the JDK, but by a bunch of other apps (see this post).
  • JAVA_TOOL_OPTIONS and _JAVA_OPTIONS are ways to specify JVM arguments as an environment variable instead of command line parameters.
    • The are picked up by at least java and javac
    • They have this precedence:
      1. _JAVA_OPTIONS (overwrites the others)
      2. Command line parameters
      3. JAVA_TOOL_OPTIONS (is overwritten by the others)

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

⇧ という質問者の記述があって、回答とかも参照すると

  1. JAVA_OPTS
  2. _JAVA_OPTIONS
  3. JAVA_TOOL_OPTIONS
  4. JDK_JAVA_OPTIONS ←JDK 9以降

の4つの環境変数なるものが出てきてるんだが、環境変数で「Java仮想マシンJVMJava Virtual Machine )」のオプションの設定のデフォルト値を恒久的に変更できるってことなんかな?

回答者の記述を見ると、

You have pretty much nailed it except that these options are picked up even if you start JVM in-process via a library call.

The fact that _JAVA_OPTIONS is not documented suggests that it is not recommended to use this variable, and I've actually seen people abuse it by setting it in their ~/.bashrc. However, if you want to get to the bottom of this problem, you can check the source of Oracle HotSpot VM (e.g. in OpenJDK7).

You should also remember that there is no guarantee other VMs have or will continue to have support for undocumented variables.

UPDATE 2015-08-04: To save five minutes for folks coming from search engines, _JAVA_OPTIONS trumps command-line arguments, which in turn trump JAVA_TOOL_OPTIONS.

https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

⇧『The fact that _JAVA_OPTIONS is not documented suggests that it is not recommended to use this variable, and I've actually seen people abuse it by setting it in their ~/.bashrc. However, if you want to get to the bottom of this problem, you can check the source of Oracle HotSpot VM (e.g. in OpenJDK7).』で、結局、ソースコードを見ろってことになるのは止めて欲しいんだが...そもそも、「src/share/vm/runtime/arguments.cpp」って、C++とか分からんから...

_JAVA_OPTIONS」の使用が非推奨であると判断するに至ったドキュメントを教えて欲しいところですな...

相変わらず、Javaもカオスですな...

何か、新しいJDKのバージョンを出して機能を追加してくれるのも良いんだけど、このあたりのドキュメントの整備の方を先に頑張って欲しい気はする...

まぁ、環境変数は対象外です、って言われてしまったらそれまでなんだけど、Javaに関係する環境変数っぽいから、面倒見て欲しいのだけどね...

何故か、

docs.oracle.com

トラブルシューティングガイドで「環境変数」が載っているのだけど、

docs.oracle.com

17.3.5 環境変数

環境変数の設定が原因で問題が発生する場合があります。バグ・レポートの作成時に、次のJava環境変数の値(設定されている場合)を記入してください。

  • JAVA_HOME

  • JRE_HOME

  • JAVA_TOOL_OPTIONS

  • _JAVA_OPTIONS

  • CLASSPATH

  • JAVA_COMPILER

  • PATH

  • USERNAME

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/bugreports003.html#CHDHHHBJ

⇧ どちらにしろ、「JAVA_OPTS」は載っていないし、そもそも、環境変数について項目名だけ羅列されてるだけで、環境変数がどう機能するのかについての説明は無いんだが...

なんだかんだで、不毛な時間を過ごしてしまったではないか...時間を返して欲しい...

毎度モヤモヤ感が半端ない...

今回はこのへんで。