一、JVM概述
Java虚拟机(Java Virtual Machine,JVM)是一种可以执行Java字节码的虚拟机,它是Java平台的核心组成部分之一。JVM负责将Java字节码翻译成特定平台上的机器指令,使得Java程序可以在各种不同的平台上运行。
JVM内存模型
- JVM内存包括堆(heap)内存、非堆(non-heap)内存。堆内存用于存储对象实例,而非堆内存用于存储Java虚拟机自身使用的数据和代码等。
- 非堆内存包括直接内存中的元空间(sdk1.8用的元空间,sdk1.7用的是线程共享的方法区)、线程私有的虚拟机栈、程序计数器、本地方法栈。
- 堆的释放受到GC垃圾回收器的管理,GC会去找那些很久没有被指向的内存块,把它们清理掉。
JVM相关数据采集间隔为15s一次,采集的是瞬时值中的最大值,总数由多个数据加和而成。
二、类加载器
(1)类加载器说明
类加载器(ClassLoader)是负责将类文件加载到内存中并进行解析和转换的组件。类加载器在Java应用程序的运行过程中起着重要的作用,它负责查找、加载、链接和初始化类。
- 加载(Loading):类加载器负责查找并加载类的字节码文件。类加载器根据类的全限定名(包括包名和类名)来定位和读取类文件的二进制数据。
- 链接(Linking):类加载器在加载类的过程中进行链接操作,包括验证、准备和解析。验证阶段确保类文件的字节码符合JVM规范;准备阶段为类的静态变量分配内存并设置默认初始值;解析阶段将符号引用转换为直接引用。
- 初始化(Initialization):在类加载完成后,类加载器会调用类的静态初始化代码块(static块)来执行类的初始化操作。这包括对静态变量进行赋值和执行静态代码块中的逻辑。
(2)类加载器类型
JVM提供了三个主要的类加载器:
- 启动类加载器(Bootstrap ClassLoader):也称为根加载器,是JVM的一部分,负责加载Java核心类库,如java.lang包中的类。它是JVM自身的一部分,由C++编写,不是一个Java类。
- 扩展类加载器(Extension ClassLoader):负责加载Java的扩展类库,位于/lib/ext目录下。它是由Java编写的,是sun.misc.Launcher$ExtClassLoader类的实例。
- 应用程序类加载器(Application ClassLoader):也称为系统类加载器,负责加载应用程序的类路径(Classpath)中指定的类。它是由Java编写的,是sun.misc.Launcher$AppClassLoader类的实例。
除了这三个主要的类加载器,JVM还支持自定义的类加载器。开发人员可以通过继承java.lang.ClassLoader类来实现自定义的类加载器,以满足特定的加载需求。
(3)类加载器的委派机制
类加载器采用委托模型(Delegation Model)来搜索和加载类。当一个类加载器需要加载一个类时,它首先将该任务委托给父类加载器。父类加载器会继续将加载请求向上委托,直到顶层的启动类加载器(Bootstrap ClassLoader)。如果父类加载器无法加载该类,子类加载器才会尝试加载。这种委托机制保证了类的唯一性和一致性,避免重复加载同一个类。
三、JVM关键监控指标
(1)GC次数和耗时
- OldGC:指的是Java虚拟机中的老年代垃圾回收,它主要处理老年代中不再使用的对象。包含次数和耗时。
- YoungGC:指的是Java虚拟机中的新生代垃圾回收,它主要处理新生代中不再使用的对象。包含次数和耗时。
(2)堆内存、非堆内存大小
- 堆内存:用于存储对象实例,包含1/3的新生代和2/3的老年代。
- 新生代:是用来存放新生的对象。 分为Eden 区、 SurvivorFrom、 SurvivorTo 三个区。
- Eden 区 :Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发 Minor GC,对新生代区进行一次垃圾回收。Servivor区不会触发Minor GC。
- ServivorFrom 区:上一次 GC 的幸存者,作为这一次 GC 的被扫描者
- ServivorTo 区:保留了一次 MinorGC 过程中的幸存者。
- 老年代:主要存放应用程序中生命周期长的内存对象。
- 非堆内存:用于存储Java虚拟机自身使用的数据和代码等等。提交字节数、初始字节数和最大字节数是指非堆内存的三个重要参数。
- 元空间:是JDK1.8及以上版本引入的概念,用来替代了永久代(PermGen)的概念。元空间主要用来存储class的元数据信息,包括类的名称、父类、接口、字段、方法等信息。
- 直接缓冲区:通常也称为直接内存(Direct Memory),它是一种可以直接访问操作系统内存空间的缓冲区。
(3)JVM线程数
- 线程总数:指当前Java虚拟机中的线程总数,包括用户线程和守护线程。
- 阻塞线程数:指当前因为等待某些条件而被阻塞的线程数量,例如等待IO操作完成、等待锁释放等。
- 死锁线程数:指当前处于死锁状态的线程数量,即两个或多个线程相互等待对方释放资源,从而导致所有线程都无法继续执行。
- 新建线程数:指当前正在创建的线程数量,这些线程尚未开始执行任何任务。
- 可运行线程数:指当前处于可运行状态的线程数量,这些线程已经准备好被调度执行,但是可能还没有得到CPU的时间片。
- 终结线程数:指已经被终止但是还没有被垃圾回收的线程数量,这些线程的run()方法已经执行完毕,但是线程对象还没有被回收。
- 限时等待线程数:指当前正在等待一段时间后才能继续执行的线程数量,例如使用Thread.sleep()方法或Object.wait(long)方法进行限时等待的线程数量。
- 等待中线程数:指当前正在等待某些条件而被挂起的线程数量,例如使用Object.wait()方法进行无限等待的线程数量。