1.概述
是否曾经想过为什么Java应用程序通过众所周知的
-Xms和-Xmx
调整标志消耗的内存比指定的数量大得多 ?由于各种原因和可能的优化,JVM可能会分配额外的本机内存。这些额外的分配最终可能使消耗的内存超出-Xmx限制。
在本教程中,我们将枚举JVM中本机内存分配的一些常见来源,以及它们的大小调整标志,然后学习如何使用本机内存跟踪来监视它们。
2.本机分配
通常,堆是Java应用程序中最大的内存消耗者,但是还有其他一些。**除了堆之外,JVM从本地内存中分配了相当大的块来维护其类元数据,应用程序代码,由JIT生成的代码,内部数据结构等。**在以下各节中,我们将探讨其中的一些分配。
2.1 元空间
为了维护有关已加载类的某些元数据,JVM使用了称为
Metaspace*****的专用非堆区域
。在Java 8之前,等效项称为PermGen或
Permanent Generation*。Metaspace或PermGen包含有关已加载类的元数据,而不是包含在堆中的有关它们的实例的元数据。
这里重要的是,由于元空间是堆外数据区域,因此堆大小调整配置不会影响元空间的大小。为了限制元空间的大小,我们使用其他调整标志:-XX:MetaspaceSize
和
-XX:MaxMetaspaceSize*设置最小和最大元空间大小
在Java 8之前,使用
-XX:PermSize
和
-XX:MaxPermSize
来设置最小和最大PermGen大小
2.2 线程数
JVM中最消耗内存的数据区域之一是堆栈,它与每个线程同时创建。堆栈存储局部变量和部分结果,在方法调用中起着重要作用。
默认的线程堆栈大小取决于平台,但是在大多数现代的64位操作系统中,大约为1 MB。此大小可通过*-Xss *调整标志进行配置。
与其他数据区域相比,当对线程数没有限制时,分配给堆栈的总内存实际上是不受限制的。 还值得一提的是,JVM本身需要一些线程来执行其内部操作,例如GC或即时编译。
2.3 代码缓存
为了在不同平台上运行JVM字节码,需要将其转换为机器指令。在执行程序时,JIT编译器负责此编译。
JVM将字节码编译为汇编指令时,会将这些指令存储在称为代码缓存的特殊非堆数据区域中 *。