Java で -Xmx や -XX:MaxRAM オプションを指定した際の最大ヒープ・サイズを調べる

## JVM の使用するメモリはヒープだけではない

[メモリ管理の概要](http://otndnld.oracle.co.jp/document/products/jrockit/geninfo/diagnos/garbage_collect.html)

> Java オブジェクトは、ヒープと呼ばれる領域に格納されています。ヒープは、JVM が起動したときに作成され、そのサイズはアプリケーションの実行中に増減することもあります。ヒープが一杯になると、ガベージ （不要なメモリ） がコレクト （収集） されます。ガベージ コレクションは、現在使用していないオブジェクトをクリアし、新しいオブジェクトのための領域を確保します。
> 
> ただし、JVM はヒープ以外にもメモリを使用します。Java メソッド、スレッド スタック、ネイティブ ハンドルはヒープ以外のメモリに割り当てられます。JVM の内部データ構造も同様です。

[Understanding Memory Management](https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/diagnos/garbage_collect.html)

> Java objects reside in an area called the heap. The heap is created when the JVM starts up and may increase or decrease in size while the application runs. When the heap becomes full, garbage is collected. During the garbage collection objects that are no longer used are cleared, thus making space for new objects.
> 
> Note that the JVM uses more memory than just the heap. For example Java methods, thread stacks and native handles are allocated in memory separate from the heap, as well as JVM internal data structures.

## -Xmx でヒープ・サイズの最大値を指定できる

[エルゴノミクス - Java Platform, Standard Edition HotSpot仮想マシン・ガベージ・コレクション・チューニング・ガイド, リリース11](https://docs.oracle.com/javase/jp/11/gctuning/ergonomics.html)

> ガベージ・コレクタが使用できる最小および最大ヒープ・サイズは、最小および最大ヒープ・サイズにそれぞれ -Xms=<nnn>および-Xmx=<mmm>を使用して設定できます。

[java - Java Platform, Standard Editionツール・リファレンス, リリース11](https://docs.oracle.com/javase/jp/11/tools/java.html)

> -Xmx size
> 
> メモリー割当てプールの最大サイズ(バイト単位)を指定します。この値は、1024の倍数で、2Mバイトより大きくなければなりません。キロバイトを指定するには文字kまたはK、メガバイトを指定するには文字mまたはM、ギガバイトを指定するには文字gまたはGを付けます。デフォルト値は、実行時にシステムの設定に基づいて選択されます。サーバー配備の場合、-Xmsと-Xmxが同一の値に設定されていることがよくあります。次の例では、様々な単位を使用して、割り当てられたメモリーの最大許容サイズを80Mバイトに設定する方法を示します。
> 
> -Xmx83886080
> -Xmx81920k
> -Xmx80m
> -Xmxオプションは-XX:MaxHeapSizeと同等です。

デフォルトでは -Xmx の最大値は物理メモリの 1/4 とのこと。

[エルゴノミクス - Java Platform, Standard Edition HotSpot仮想マシン・ガベージ・コレクション・チューニング・ガイド, リリース11](https://docs.oracle.com/javase/jp/11/gctuning/ergonomics.html)

> 次に、重要なガベージ・コレクタ、ヒープ・サイズおよびランタイム・コンパイラのデフォルト選択を示します。 
> 
> ・ガベージファースト(G1)・コレクタ
> ・GCスレッドの最大数は、ヒープ・サイズと使用可能なCPUリソースによって制限されます 
> ・初期ヒープ・サイズ: 物理メモリーの1/64 
> ・最大ヒープ・サイズ: 物理メモリーの1/4 
> ・C1とC2の両方を使用する階層型コンパイラ 

## -XX:MaxRAM で物理メモリをシミュレートする

-XX:MaxRAM は、OSなどの動作環境が使用できるメモリの最大値を指定するパラメータ。物理メモリの値と同等の意味を持つ。
-XX:MaxRam500m を指定すると、ヒープの最大サイズは約250MBになる。

[OpenJDK and Containers \- Red Hat Developer](https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers/)

> Why is it when I specify -Xmx=1g my JVM uses up more memory than 1gb of memory?
> 
> Specifying -Xmx=1g is telling the JVM to allocate a 1gb heap. It’s not telling the JVM to limit its entire memory usage to 1gb. There are card tables, code caches, and all sorts of other off heap data structures. The parameter you use to specify total memory usage is -XX:MaxRAM. Be aware that with -XX:MaxRam=500m your heap will be approximately 250mb.

[第41章 アプリケーションメモリーのサイジング OpenShift Container Platform 3\.9 \| Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.9/html/developer_guide/dev-guide-application-memory-sizing)

> 41.4.1. JVM 最大ヒープサイズの上書き
> 
> 数多くの Java ワークロードにおいて、JVM ヒープはメモリーの最大かつ単一のコンシューマーです。現時点で OpenJDK は、OpenJDK がコンテナー内で実行されているかにかかわらず、ヒープに使用されるコンピュートノードのメモリーの最大 1/4 (1/-XX:MaxRAMFraction) を許可するようデフォルトで設定されます。そのため、コンテナーのメモリー制限も設定されている場合には、この動作をオーバーライドすることが 必須 です。
> 
> 上記を実行する方法として、2 つ以上の方法を使用できます。
> 
> 1. コンテナーのメモリー制限が設定されており、JVM で実験的なオプションがサポートされている場合には、-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap を設定します。
> 
> これにより、-XX:MaxRAM がコンテナーのメモリー制限に設定され、最大ヒープサイズ (-XX:MaxHeapSize / -Xmx) が 1/-XX:MaxRAMFraction に設定されます (デフォルトでは 1/4)。
> 
> 2. -XX:MaxRAM、-XX:MaxHeapSize または -Xmx のいずれかを直接上書きします。
> 
> このオプションには、値のハードコーディングが必要になりますが、安全マージンを計算できるという利点があります。

## -XX:+PrintFlagsFinal で最大ヒープサイズを確認する

[パラレル・コレクタ - Java Platform, Standard Edition HotSpot仮想マシン・ガベージ・コレクション・チューニング・ガイド, リリース11](https://docs.oracle.com/javase/jp/11/gctuning/parallel-collector1.html)

> アプリケーションがうまく動作するために必要なヒープ領域がわかっている場合は、-Xmsと-Xmxを同じ値に設定できます。そうでない場合は、JVMが初期ヒープ・サイズを使用して開始され、ヒープ使用量とパフォーマンスのバランスが取れるまで、Javaヒープが増加します。
> 
> これらのデフォルト値は、他のパラメータおよびオプションの影響を受ける場合があります。デフォルト値を確認するには、-XX:+PrintFlagsFinalオプションを使用し、出力から-XX:MaxHeapSizeを探します。たとえば、LinuxまたはSolarisでは、次を実行できます。
> 
> java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize

-XX:+PrintFlagsFinal オプションの挙動を確認してみる。

搭載メモリ8GBのマシンで確認したところ MaxHeapSize が2GB程度に設定されているのを確認できた。

```
$ java -XX:+PrintFlagsFinal -version | grep -Ei "maxheapsize | maxram"
   size_t MaxHeapSize                              = 2147483648                                {product} {ergonomic}
 uint64_t MaxRAM                                   = 137438953472                           {pd product} {default}
    uintx MaxRAMFraction                           = 4                                         {product} {default}
   double MaxRAMPercentage                         = 25.000000                                 {product} {default}
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
```

-Xmx256m を指定したら MaxHeapSize が256MB程度に設定された。

```
$ java -XX:+PrintFlagsFinal -Xmx256m -version | grep -Ei "maxheapsize | maxram"
   size_t MaxHeapSize                              = 268435456                                 {product} {command line}
 uint64_t MaxRAM                                   = 137438953472                           {pd product} {default}
    uintx MaxRAMFraction                           = 4                                         {product} {default}
   double MaxRAMPercentage                         = 25.000000                                 {product} {default}
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
```

-XX:MaxRAM=256M を指定したら MaxHeapSize が128MB程度に設定された。

```
$ java -XX:+PrintFlagsFinal -XX:MaxRAM=256M -version | grep -Ei "maxheapsize | maxram"
   size_t MaxHeapSize                              = 132120576                                 {product} {ergonomic}
 uint64_t MaxRAM                                   = 268435456                              {pd product} {command line}
    uintx MaxRAMFraction                           = 4                                         {product} {default}
   double MaxRAMPercentage                         = 25.000000                                 {product} {default}
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
```



## Java のプログラムでメモリ情報を取得

JavaMemory.java 

```java
public class JavaMemory {
  public static void main(String[] args) {
    Runtime r = Runtime.getRuntime();
    System.out.println("version    : " + r.version());
    System.out.println("maxMemory  : " + r.maxMemory());
    System.out.println("totalMemory: " + r.totalMemory());
  }
}
```

初期ヒープサイズ32MB、最大ヒープサイズ256MBを指定した場合の実行例。
maxMemoryが256MB、totalMemoryが32MB程度になっている。

```
$ java -Xms32M -Xmx256M JavaMemory
version    : 11.0.2+9
maxMemory  : 268435456
totalMemory: 33554432
```

初期ヒープサイズ256MB、最大ヒープサイズ256MBを指定した場合の実行例。
maxMemoryが256MB、totalMemoryが256MB程度になっている。

```
$ java -Xms256M -Xmx256M JavaMemory
version    : 11.0.2+9
maxMemory  : 268435456
totalMemory: 268435456
```

物理メモリサイズ256MBを指定した場合の実行例。
maxMemoryが128MB、totalMemoryが8MB程度になっている。

```
$ java -XX:MaxRAM=256M JavaMemory
version    : 11.0.2+9
maxMemory  : 132120576
totalMemory: 8388608
```

## 参考資料
- [Oracle JRockit JVM 診断ガイド](http://otndnld.oracle.co.jp/document/products/jrockit/geninfo/diagnos/)
- [Oracle JRockit JVM Diagnostics Guide](https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/diagnos/)
- [Java Platform, Standard Edition HotSpot仮想マシン・ガベージ・コレクション・チューニング・ガイド, リリース11](https://docs.oracle.com/javase/jp/11/gctuning/)
- [OpenJDK and Containers \- Red Hat Developer](https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers/)
- [第41章 アプリケーションメモリーのサイジング OpenShift Container Platform 3\.9 \| Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.9/html/developer_guide/dev-guide-application-memory-sizing)