详解详解JVM中的本机内存跟踪中的本机内存跟踪
在本文里小编给大家整理了一篇关于JVM中的本机内存跟踪的相关知识点内容,有兴趣的朋友们参考学习下。
1.概述概述
有没有想过为什么Java应用程序通过众所周知的-Xms和-Xmx调优标志消耗的内存比指定数量多得多?出于各种原因和可能的
优化,JVM可以分配额外的本机内存。这些额外的分配最终会使消耗的内存超出-Xmx限制。
在本教程中,我们将列举JVM中的一些常见内存分配源,以及它们的大小调整标志,然后学习如何使用本机内存跟踪监视它
们。
2.原生分配原生分配
堆通常是Java应用程序中最大的内存使用者,但还有其他人。除了堆之外,JVM还从本机内存中分配出一个相当大的块来维
护类的元数据,应用程序代码,JIT生成的代码,内部数据结构等。在下面的部分中,我们将探讨其中的一些分配。
2.1. Metaspace(元空间)
为了维护有关已加载类的一些元数据,JVM使用名为Metaspace的专用非堆区域。在Java 8之前,被称为PermGen或
Permanent Generation。 Metaspace或PermGen包含有关已加载类的元数据,而不是它们的实例,它们保存在堆中。
这里重要的是堆大小配置不会影响元空间大小,因为Metaspace是一个堆外数据区。为了限制Metaspace大小,我们使用其他
调优标志:
-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置最小和最大元空间大小
在Java 8之前,-XX:PermSize和-XX:MaxPermSize设置最小和最大PermGen大小
2.2. Threads(线程)
JVM中最耗费内存的数据区之一是堆栈,与每个线程同时创建。堆栈存储局部变量和部分结果,在方法调用中起着重要作用。
默认的线程堆栈大小取决于平台,但在大多数现代64位操作系统中,它大约为1 MB。此大小可通过-Xss调整标志进行配置。
与其他数据区域相比,当对线程数没有限制时,分配给堆栈的总内存实际上是无限制的。值得一提的是,JVM本身需要一些线
程来执行其内部操作,如GC或即时编译。
2.3. Code Cache(代码缓存)
为了在不同平台上运行JVM字节码,需要将其转换为机器指令。执行程序时,JIT编译器负责此编译。
当JVM将字节码编译为汇编指令时,它会将这些指令存储在称为代码缓存的特殊非堆数据区中。可以像管理JVM中的其他数据
区一样管理代码缓存。 -XX:InitialCodeCacheSize和-XX:ReservedCodeCacheSize调整标志确定代码缓存的初始值和可能
最大值。
2.4. Garbage Collection(垃圾回收)
JVM附带了一些GC算法,每个算法适用于不同的用例。所有这些GC算法都有一个共同的特点:他们需要使用一些堆外数据结
构来执行他们的任务。这些内部数据结构消耗更多本机内存。
2.5. Symbols(符号)
让我们从 Strings 开始,这是应用程序和库代码中最常用的数据类型之一。由于它们无处不在,它们通常占据堆的很大一部
分。如果大量的这些字符串包含相同的内容,那么堆的很大一部分将被浪费。
为了节省一些堆空间,我们可以存储每个 String 的一个版本,并让其他版本引用存储的版本。此过程称为 String Interning 。
由于JVM只能内部编译时间字符串常量,我们可以手动调用字符串的intern方法来获取内部编译字符串。
JVM将实际存储的字符串存储在本机特殊固定大小并称为字符串表的哈希表中,也称为字符串池。我们可以通过-XX:
StringTableSize调整标志配置表大小(即桶的数量)。
除了字符串表之外,还有另一个称为运行时常量池的本机数据区域。 JVM使用此池来存储常量,如编译时数字文字或必须在
运行时解析的方法和字段引用。
2.6. Native Byte Buffers(本地字节缓冲区)
JVM通常有大量分配本机内存的嫌疑,但有时开发人员也可以直接分配本机内存。最常见的方法是被JNI调用的malloc和NIO
中可直接调用的ByteBuffers。
2.7. Additional Tuning Flags(额外的调整标志)