Java虚拟机(Java Virtual Machine,JVM)是一种可以执行Java字节码的虚拟机,它是Java平台的核心组成部分之一。JVM负责将Java字节码翻译成特定平台上的机器指令,使得Java程序可以在各种不同的平台上运行。
JVM监控即Java虚拟机监控,用于监控重要的JVM指标。
- JVM内存包括堆(heap)内存、非堆(non-heap)内存。堆内存用于存储对象实例,而非堆内存用于存储Java虚拟机自身使用的数据和代码等。
- 非堆内存包括直接内存中的元空间(sdk1.8用的元空间,sdk1.7用的是线程共享的方法区)、线程私有的虚拟机栈、程序计数器、本地方法栈。
- 堆的释放受到GC垃圾回收器的管理,GC会去找那些很久没有引用地址指向的内存块,把它们清理掉。
说明JVM相关数据采集间隔为15s一次,采集的是瞬时值中的最大值,总数由多个数据加和而成。以堆内存图表为例,1分钟内我们分别采集了老年代、年轻代各4次,取其中的最大值展示,总和为最大值之和(因此总和可能大于用户配置的jvm内存大小)。
功能入口
- 选择目标资源池,并登录APM组件控制台。
- 在左侧导航栏中选择「应用监控」-「应用列表」。
- 在应用列表中选择您想查看的应用,点击「应用名称」打开新的应用详情链接。
- 在左侧导航栏中选择「应用详情」,您可以在应用详情页面切换至「JVM监控」页签,在左侧关键指标中选择不同的应用实例,可查看该应用实例相应的概览信息。
功能说明
内存
堆内存详情
堆内存用于存储对象实例,包含1/3的新生代和2/3的老年代。
- 新生代:是用来存放新生的对象。 分为Eden 区、 SurvivorFrom、 SurvivorTo 三个区。Minor GC进行垃圾回收。JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
- Eden 区 :Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发 Minor GC,对新生代区进行一次垃圾回收。Servivor区不会触发Minor GC。
- ServivorFrom 区:上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
- ServivorTo 区:保留了一次 MinorGC 过程中的幸存者。
- 老年代:主要存放应用程序中生命周期长的内存对象。 Major GC进行垃圾回收。可能会抛出OOM(Out Of Memory)异常
非堆内存
非堆内存用于存储Java虚拟机自身使用的数据和代码等等。提交字节数、初始字节数和最大字节数是指非堆内存的三个重要参数,具体含义如下
- 提交字节数(Committed Bytes):指已经被操作系统分配给Java虚拟机的非堆内存大小,包括已经使用的和未使用的部分,是在Java虚拟机运行时动态确定的。
- 初始字节数(Initial Bytes):指Java虚拟机启动时申请的非堆内存大小,也就是Java虚拟机最初分配的非堆内存大小。
- 最大字节数(Max Bytes):指Java虚拟机能够申请的非堆内存的最大值。当Java虚拟机需要更多的非堆内存时,可以向操作系统请求更多的内存,直到达到最大字节数为止。
这些指标可以用来观察非堆内存的大小变化,以便我们根据具体的应用程序调整非堆内存的参数设置(例如使用“-XX:MaxDirectMemorySize”参数来设置非堆内存的最大字节数等等),以保证应用程序能够正常运行,并且不会造成不必要的内存浪费。
元空间详情
- 元空间(Metaspace):是JDK1.8及以上版本引入的概念,用来替代了永久代(PermGen)的概念。元空间主要用来存储class的元数据信息,包括类的名称、父类、接口、字段、方法等信息。元空间不再像永久代那样有固定的大小,而是使用本地内存来存储元数据。通过设置元空间大小参数,可以控制元空间的大小。当元空间不足时,JVM会自动扩容。元空间的大小仅受本地内存限制。
说明永久代(PermGen)是JDK1.7及以下版本中的概念,用来存储类信息和常量池。永久代的大小是固定的,由JVM启动时指定。当永久代空间不足时,会导致OutOfMemoryError异常。
直接缓冲区
直接缓冲区(Direct Buffer)通常也称为直接内存(Direct Memory),它是一种可以直接访问操作系统内存空间的缓冲区。与普通的Java NIO缓冲区不同,直接缓冲区不需要将数据从Java堆内存复制到直接内存,而是直接将数据存储在直接内存中,从而避免了数据拷贝的开销,提高了IO操作的效率。
- DirectBuffer总大小:指已经被Java虚拟机分配的所有DirectBuffer缓冲区的总大小,包括已经使用的和未使用的部分。
- DirectBuffer使用大小:指当前正在被使用的DirectBuffer缓冲区的总大小。
DirectBuffer总大小和使用大小都是在运行时动态确定的。这些指标可以用来判断直接缓冲区的使用情况。由于直接内存不受Java虚拟机的垃圾回收机制控制,所以如果不及时释放直接内存,可能会导致内存泄漏或者OutOfMemoryError等问题。因此,我们需要关注直接内存的使用情况,以便及时释放不再需要的内存空间,可以使用ByteBuffer的cleaner()方法或者手动调用System.gc()方法来释放直接内存。
GC
GC累计次数与GC瞬时次数
提供累计/瞬时两种视角查看。
- OldGC次数:指的是Java虚拟机中的老年代垃圾回收次数。它主要处理老年代中不再使用的对象。
- YoungGC次数:指的是Java虚拟机中的新生代垃圾回收次数。它主要处理新生代中不再使用的对象。
GC累计耗时与GC瞬时耗时
提供累计/瞬时两种视角查看。
- OldGC耗时:指的是Java虚拟机中的老年代垃圾回收累计花费的时间。
- YoungGC耗时:指的是Java虚拟机中的新生代垃圾回收累计花费的时间。
线程
线程(Thread)是操作系统能够进行运算调度的最小单位,是程序执行的基本单元。在Java中,每个线程都是一个独立的执行路径,它可以独立地执行代码、访问变量和资源,与其他线程并发执行。
JVM线程数
JVM线程数是指在Java虚拟机中活跃的线程数,也就是当前正在执行的Java线程数量,包括用户线程(比如计算、IO操作等等)和守护线程(比如垃圾回收、内存监控等等)。
- 线程总数:指当前Java虚拟机中的线程总数,包括用户线程和守护线程。
- 阻塞线程数:指当前因为等待某些条件而被阻塞的线程数量,例如等待IO操作完成、等待锁释放等。
- 死锁线程数:指当前处于死锁状态的线程数量,即两个或多个线程相互等待对方释放资源,从而导致所有线程都无法继续执行。
- 新建线程数:指当前正在创建的线程数量,这些线程尚未开始执行任何任务。
- Runnable线程数(可运行线程数):指当前处于可运行状态的线程数量,这些线程已经准备好被调度执行,但是可能还没有得到CPU的时间片。
- 终结线程数:指已经被终止但是还没有被垃圾回收的线程数量,这些线程的run()方法已经执行完毕,但是线程对象还没有被回收。
- Timed_Waiting的线程数(限时等待线程数):指当前正在等待一段时间后才能继续执行的线程数量,例如使用Thread.sleep()方法或Object.wait(long)方法进行限时等待的线程数量。
- Waiting的线程数(等待中线程数):指当前正在等待某些条件而被挂起的线程数量,例如使用Object.wait()方法进行无限等待的线程数量。
这些指标可以用来监控Java虚拟机中线程状态的变化,以及分析线程运行情况和性能瓶颈。
JVM类总数
- 已加载类总数:指的是当前已经被Java虚拟机(JVM)加载的类的总数。当JVM加载一个类时,它会对该类进行解析、验证和准备,并将其放入方法区(或称为永久代或元空间,具体取决于JVM的版本和配置)。已加载类总数表示了在JVM启动以来,已经成功加载到内存中的类的数量。
- 已卸载类总数:指的是从JVM中卸载(或称为卸载垃圾收集)的类的总数。在某些情况下,JVM可能会卸载已加载的类,例如当类的实例被垃圾收集器判定为不再可达时。已卸载类总数表示自JVM启动以来被卸载的类的数量。
- 当前加载类总数:指的是当前在JVM中仍然保留的已加载类的数量。它包括尚未被卸载的类以及仍然被JVM使用的类。这个数字可以随着应用程序的进行而变化,因为新的类可以被动态地加载到JVM中,而一些不再使用的类可以被卸载。
统一交互操作说明:
- 将光标移到统计图上,可以查看光标所至时间点的数据详情。
- 单击图标,可以将当前图表放大显示。