25

metaspace存放什么数据

 3 years ago
source link: http://javakk.com/831.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Java堆内存并不占JVM进程内存分配的100%。在JVM进程中有许多种类的非堆内存,当对它们进行汇总时,它们通常占比堆更多的RAM。可以将最大堆大小设置为512MB, -Xmx512m ,并使进程总共消耗超过1GB的RAM。

如果您对学习非堆类别不感兴趣,可以跳转到改进JVM内存使用的建议,以获得一些实用的技巧。但是,如果您通读这篇文章,您将了解Java进程内存的去向以及原因。让我们看看这些目的地。

JVM内存类别

最重要的JVM内存类别有:

  • Heap -堆是存储类实例化或“对象”的地方。
  • Thread stacks 线程堆栈 -每个线程都有自己的调用堆栈。堆栈存储原始局部变量和对象引用以及调用堆栈(方法调用列表)本身。当堆栈帧移出上下文时,堆栈将被清理,因此此处不执行GC。
  • Metaspace 元空间 -Metaspace存储对象的类定义和其他一些元数据。
  • Code cache 代码缓存 -JIT编译器将它生成的本机代码存储在代码缓存中,通过重用来提高性能。
  • Buffer pools 缓冲池 -许多库和框架在堆外分配缓冲区以提高性能。这些缓冲池可以用来在Java代码和本机代码之间共享内存,或者将文件的区域映射到内存中。
  • 操作系统内存 ——操作系统保持Java进程的堆和堆栈独立于JVM本身管理的堆和堆栈。加载的每个本机库也会消耗内存(例如libjvm.so文件). 这个通常很小。

JConsole和 VisualVM 可以帮助您检查其中一些类别。但是即使是这些工具也不能很好地捕获缓冲池,因为缓冲池是非堆内存使用的 最危险的来源 。让我们看一个例子。

直接缓冲池 Direct Buffer Pools

将以下代码放入名为 Main.java 类:

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

class Main {
  public static void main(String[] args) throws Exception {
    while (true) {
      List<ByteBuffer> buffers = new ArrayList<>();
      for (int i=0; i < 60; i++) {
        buffers.add(ByteBuffer.allocateDirect(5120000));
      }
    }
  }
}

这将创建一个无限循环,该循环分配许多直接缓冲区,然后释放它们。要运行它,请执行以下命令:

$ javac Main.java
$ java -Xmx512m Main

让进程继续运行,并通过在另一个终端中执行 JConsole 打开JConsole。然后连接到名为“ Main ”的应用程序。单击Memory选项卡,您将看到堆内存是稳定的(可能远低于20 MB)。

v6fyeme.png!mobile

切换到“非堆”,您将看到类似的内容。但是如果你在操作系统级别检查内存,你会得到不同的结果。在Linux和Mac上,可以运行 jps 来查找进程ID( PID ),然后运行:

$ top -pid <PID>

top 命令将在MEM列中显示Java进程的总内存,如下所示:

PID    COMMAND  %CPU  TIME     #TH  #WQ  #POR MEM   PURG CMPR PGRP  PPID
69959  java     133.5 00:14.41 17/1 0    71-  432M+ 0B   0B   69959 11514

这个过程实际上消耗了 432MB的内存 !但我们在JConsole中看不到这一点,因为调用 ByteBuffer.allocateDirect 在缓冲池中分配内存。通过单击MBean选项卡,然后选择 java.nio.BufferPool 名为“直接”的MBean。你会看到这样的画面:

vmY3Ivv.png!mobile

直接字节缓冲区对于提高性能很重要,因为它们允许本机代码和Java代码在不复制数据的情况下共享数据。但是 allocateDirect 方法调用的代价很高,这意味着字节缓冲区在创建之后通常会被重用。因此,有些框架会在流程的生命周期中保留它们。

您不太可能需要自己使用 allocateDirect 。但使用调用此方法的框架是非常常见的。一个例子是 Netty ,它被Play和Ratpack等流行的web框架使用。

不过,直接内存并不是隐藏JVM内存消耗的唯一来源。另一个罪魁祸首是元空间。

观察元空间Metaspace

Metaspace包含有关JVM正在运行的应用程序的元数据。它包含类定义、方法定义和有关程序的其他信息。加载到应用程序中的类越多,元空间就越大。

在旧版本的Java中,类元数据存储在堆中,这意味着对于一般开发人员来说,它并不是那么不可见。但是随着java8中Metaspace的引入,我们必须小心地显式地观察它。

大多数Java应用程序运行时的元空间都不到100mb,但是JRuby和Scala等其他JVM语言通常最多只能使用200mb。这是因为这些语言本质上是非常大的框架。他们正在Java标准库的顶部加载一个完整的标准库。

要演示这一点,请启动一个Scala REPL并在其中执行一些命令,如下所示(您可以通过运行 sdk install Scala 来使用SDKMAN安装Scala):

$ scala
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.

scala> val abcde = List('a', 'b', 'c', 'd', 'e')
abcde: List[Char] = List(a, b, c, d, e)

REPL 保持运行状态,并在另一个终端中执行 jconsole 以打开jconsole。然后选择“MainGenericRunner”进程并连接到它。单击“内存”选项卡并从下拉列表中选择“非堆内存”。您将看到该进程已经消耗了将近70MB的非堆内存。

AZBNBv.png!mobile

这些内存的大部分被元空间(可能是40到50 MB)消耗,您可以在 VisualVM 中看到:

IBFjIra.png!mobile

这种情况下,您通常无能为力,但您需要小心,以确保堆设置为元空间留出足够的空间。不过,在某些情况下,您将需要以比VisualVM或JConsole所能提供的更精细的粒度来分解非堆内存。幸运的是,有一些工具可以做到这一点。

使用本机内存跟踪

本机内存跟踪( NMT )是一个JVM特性,用于跟踪内部内存使用情况。要启用它,请将以下选项添加到用于运行应用程序的java命令中:

-XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics

例如,可以运行先前创建的主类:

$ java -XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics Main

然后获取Java进程的PID,并使用 jcmd 打印出进程的本机内存使用情况:

$ jcmd <PID> VM.native_memory summary
Native Memory Tracking:

Total: reserved=3554519KB, committed=542799KB
-                 Java Heap (reserved=2097152KB, committed=372736KB)
                            (mmap: reserved=2097152KB, committed=372736KB)

-                     Class (reserved=1083039KB, committed=38047KB)
                            (classes #5879)
                            (malloc=5791KB #6512)
                            (mmap: reserved=1077248KB, committed=32256KB)

-                    Thread (reserved=22654KB, committed=22654KB)
                            (thread #23)
                            (stack: reserved=22528KB, committed=22528KB)
                            (malloc=68KB #116)
                            (arena=58KB #44)

-                      Code (reserved=251925KB, committed=15585KB)
                            (malloc=2325KB #3622)
                            (mmap: reserved=249600KB, committed=13260KB)

-                        GC (reserved=82398KB, committed=76426KB)
                            (malloc=5774KB #182)
                            (mmap: reserved=76624KB, committed=70652KB)

-                  Compiler (reserved=139KB, committed=139KB)
                            (malloc=9KB #128)
                            (arena=131KB #3)

-                  Internal (reserved=6127KB, committed=6127KB)
                            (malloc=6095KB #7439)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=9513KB, committed=9513KB)
                            (malloc=6724KB #60789)
                            (arena=2789KB #1)

-    Native Memory Tracking (reserved=1385KB, committed=1385KB)
                            (malloc=121KB #1921)
                            (tracking overhead=1263KB)

-               Arena Chunk (reserved=186KB, committed=186KB)
                            (malloc=186KB)

jcmd 工具打印每个内存类别的详细分配信息。但它不捕获直接或映射的缓冲池。元空间,主要由“类”类别表示。

通常,jcmd转储本身只起到中等作用。更常见的做法是通过运行 jcmd <PID> VM.native_memory summary.diff

这是调试内存问题的一个很好的工具,但对于在生产应用程序上被动地收集遥测数据来说,它不是一个好工具。为此,您需要使用Heroku Java代理读取内存日志记录。

改进JVM内存使用的建议

以下是一些改善 JVM 进程内存占用的一般建议:

  1. 将最大线程堆栈大小设置为更小。 -Xss512k 很常见,但是在64位JVM上可以低到 -Xss256k
  2. 查找未关闭的IO流。确保在 finally 子句中可能关闭了任何InputStream或OutputStream对象。不这样做会导致堆外内存泄漏,就像文章: http://javakk.com/160.html 所描述的那样。
  3. 将最大堆大小( -Xmx )设置为较低。太多的应用程序运行的堆远远大于它们需要的堆。堆太大实际上会影响性能,因为它会导致GC变慢,然后在运行时强制它超时工作(通常会导致长时间的暂停)。
  4. 调整 glibc ,方法是将 MALLOC_ARENA_MAX 设置为低于默认值的值,该值是CPU内核数的8倍。将此变量设置为“2”或“1”会导致内存池更少,内存可能更少,但这可能会降低性能。

更多关于metaspace存储什么的文章请参考这篇: http://javakk.com/395.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK