java -Xmx1G 是指 10^9 还是 2^30 字节?

Does java -Xmx1G mean 10^9 or 2^30 bytes?

一般来说,-Xmx-Xms-Xmn 选项("k"、"M" 和 "G",或不太标准的可能性 "K"、"m" 或 "g") Binary prefix 倍数(即 1024 的幂),或者它们是 1000 的幂?

手册上说它们代表千字节 (kB)、兆字节 (MB) 和千兆字节 (GB),表明它们是原文定义的 1000 的幂 SI system. My informal tests (that I'm not very confident about) suggest they are really kibibytes (kiB), mebibytes (MiB) and gibibytes (GiB),都是 1024 的幂。

所以哪个是对的?例如。什么 Java 代码会显示当前尺寸?

对于 RAM 大小使用 1024 的倍数并不奇怪,因为 RAM 通常是通过加倍硬件模块来物理布局的。但是,随着我们获得越来越大的幂,以清晰和标准的方式使用单位变得越来越重要,因为混淆的可能性越来越大。 "t" 单位也被我的 JVM 接受,1 TiB 比 1 TB 大 10%。

注意:如果这些确实是二进制倍数,我建议更新文档和用户界面以使其非常清楚,例如“附加字母 k 或 K 以表示千字节(1024 字节) ), 或 m 或 M 表示兆字节(1048576 字节)”。这是采用的方法,例如,在 Ubuntu 中:UnitsPolicy - Ubuntu Wiki.

注意:有关选项用途的更多信息,请参见例如java - What are the Xms and Xmx parameters when starting JVMs?.

您有两种选择来获得问题的答案:

a) 检查 JDK 的源代码。抱歉,我无法在 5 分钟内 google。

b) 编写一个模拟,运行 多次并进行一些观察。

public class A {
  public static void main(String[] args) throws Exception {
    System.out.println("total: " + Runtime.getRuntime().totalMemory());
  }
}

和运行它几次:

java -Xms130m -Xmx2G A
total: 131072000
java -Xms131m -Xmx2G A
total: 132644864
java -Xms132m -Xmx2G A
total: 132644864
java -Xms133m -Xmx2G A
total: 134742016
java -Xms134m -Xmx2G A
total: 134742016

所以有根据的猜测是 java 使用的不是精确数字,而是您请求的数字的 2^n 近似值。

在另一个关于-Xmx标志的question中,Runtime.getRuntime().maxMemory()用于显示当前大小。还注意到 -Xmx1024m-Xmx1g 产生相同的输出,表明数字是 2 的幂而不是 10。

还要注意 totalMemory()maxMemory() 之间的区别。
What are Runtime.getRuntime().totalMemory() and freeMemory()?

简答:JVM 命令行参数使用的所有内存大小均以传统二进制单位指定,其中千字节为 1024 字节,其他为 1024 的递增幂。

长答案:

This documentation page on the command line arguments 表示以下适用于所有接受内存大小的参数:

For example, to set the size to 8 GB, you can specify either 8g, 8192m, 8388608k, or 8589934592 as the argument.

对于-Xmx,它给出了这些具体的例子:

The following examples show how to set the maximum allowed size of allocated memory to 80 MB using various units:

-Xmx83886080
-Xmx81920k
-Xmx80m

在我想查看文档之前(我假设你已经有了?),我检查了 HotSpot 的源代码,发现内存值在 src/share/vm/runtime/arguments.cpp 中被函数 atomull 解析(其中似乎代表 "ASCII to memory, unsigned long long"):

// Parses a memory size specification string.
static bool atomull(const char *s, julong* result) {
  julong n = 0;
  int args_read = sscanf(s, JULONG_FORMAT, &n);
  if (args_read != 1) {
    return false;
  }
  while (*s != '[=10=]' && isdigit(*s)) {
    s++;
  }
  // 4705540: illegal if more characters are found after the first non-digit
  if (strlen(s) > 1) {
    return false;
  }
  switch (*s) {
    case 'T': case 't':
      *result = n * G * K;
      // Check for overflow.
      if (*result/((julong)G * K) != n) return false;
      return true;
    case 'G': case 'g':
      *result = n * G;
      if (*result/G != n) return false;
      return true;
    case 'M': case 'm':
      *result = n * M;
      if (*result/M != n) return false;
      return true;
    case 'K': case 'k':
      *result = n * K;
      if (*result/K != n) return false;
      return true;
    case '[=10=]':
      *result = n;
      return true;
    default:
      return false;
  }
}

那些常量KMG定义在src/share/vm/utilities/globalDefinitions.hpp:

const size_t K                  = 1024;
const size_t M                  = K*K;
const size_t G                  = M*K;

所有这些都证实了文档,除了对 TB 的 T 后缀的支持显然是后来添加的,根本没有记录。

使用单位乘数不是强制性的,所以如果你想要十亿字节,你可以写-Xmx1000000000。如果你确实使用乘法器,它们是二进制的,所以 -Xmx1G 意味着 230 字节,或者一根 RAM。

(这并不奇怪,因为 Java 早于 IEC 尝试追溯重新定义现有单词的尝试。如果 IEC 只是建议用限定符 "binary" 和 "decimal" 有时它们的含义不清楚。例如,二进制千兆字节 (GB2) = 10243 字节,以及 十进制千兆字节 (GB10) = 10003 字节。但不,他们重新定义了每个人都在使用的词,不可避免地引发了混乱,让我们陷入了这些小丑术语 "gibibyte"、"tebibyte" 和其他术语中。哦上帝保佑我们.)