当前位置: 首页 > news >正文

黑马程序员JVM基础学习笔记

本文为该视频基础篇的学习笔记: 黑马程序员JVM虚拟机入门到实战全套视频教程,java大厂面试必会的jvm一套搞定(丰富的实战案例及最热面试题)

JVM基础学习笔记

一、JVM核心概念

1.1 什么是JVM

JVM(Java Virtual Machine,Java虚拟机)是运行Java字节码的虚拟计算机,是连接Java源代码与底层操作系统的中间层,其核心能力包括:

  • 解释运行:实时将字节码指令翻译为机器码并执行,实现“一次编写,到处运行”的跨平台特性。
  • 内存管理:自动为对象、方法分配内存,并通过垃圾回收机制回收不再使用的对象,减少手动内存管理的风险。
  • 即时编译(JIT):对频繁执行的热点代码进行编译优化,将字节码直接转换为机器码缓存,提升运行性能。

1.2 JIT即时编译的演进

  • JDK1.1引入JIT:解决了解释执行效率低的问题。原理是将多次运行的代码(热点代码)编译为机器码并保存在内存中,后续调用时直接从内存获取机器码执行,避免重复解释。
  • 现代JIT优化:不仅缓存编译结果,还会通过方法内联、循环展开等优化手段进一步提升代码执行效率,是JVM性能优化的核心机制之一。

1.3 常见JVM实现

不同厂商基于JVM规范实现了多种虚拟机,适用于不同场景:

  • HotSpot:Oracle JDK和OpenJDK的默认虚拟机,采用解释执行与JIT编译混合模式,性能优异,应用最广泛。
  • GraalVM:Oracle推出的跨语言虚拟机,支持Java、JavaScript、Python等多种语言,同时提供AOT(提前编译)能力,适合微服务与云原生场景。
  • Dragonwell JDK:阿里巴巴基于OpenJDK定制的虚拟机,针对电商高并发、大数据场景优化,增强了稳定性和性能。

二、JVM组成结构

JVM的核心组件包括类加载器、运行时数据区、执行引擎和本地接口,协同完成字节码的加载、执行与内存管理。

2.1 类加载器(ClassLoader)

负责将字节码文件(.class)加载到内存,是连接字节码与JVM的桥梁。

2.2 运行时数据区

JVM在运行过程中管理的内存区域,分为线程私有和线程共享两类(详见“四、运行时数据区”)。

2.3 执行引擎

  • 解释器:实时翻译字节码为机器码,启动快但执行效率低。
  • JIT编译器:对热点代码编译优化,执行效率高但启动时有额外开销。
  • 垃圾回收器:回收线程共享区域中不再使用的对象,释放内存。

2.4 本地接口(Native Interface)

用于调用非Java语言实现的本地方法(如C/C++代码),通过JNI(Java Native Interface)实现Java与底层系统的交互。

三、字节码与类加载机制

3.1 字节码文件的组成

字节码是源代码编译后的二进制文件(.class),是JVM的“机器语言”,其结构包括:

  1. 基础信息
    • 魔数:文件头4字节固定为cafebabe,用于校验.class文件合法性。
    • JDK版本号:主版本号=JDK版本+44(如JDK8对应主版本号52),用于保证兼容性。
    • 统计计数与索引:记录常量池、字段、方法等组件的数量与位置。
  2. 常量池
    • 存储字符串常量、类/接口名、字段名等共享数据,避免重复定义以节省空间。
  3. 字段表
    • 描述类的成员变量(包括静态变量和实例变量),包含访问修饰符、数据类型、名称等信息。
  4. 方法表
    • 描述类的方法,包含方法名、参数列表、返回值类型,以及方法体对应的字节码指令(如istoreiloadiconst等)。
    • 每个方法对应一个栈帧,包含局部变量表、操作数栈等运行时数据(详见“栈帧的组成”)。
  5. 属性表
    • 存储额外信息,如代码行号映射、异常处理表、注解等。

3.2 字节码常用工具

  1. javap:JDK自带命令行工具,通过javap -v 类名.class可查看字节码详细信息(包括常量池、方法指令等),适合服务器环境使用。
  2. jclasslib:IDEA插件,可视化展示字节码结构,支持查看常量池、方法指令、属性等,适合开发阶段分析。
  3. Arthas:阿里开源的诊断工具,提供dashboard(监控面板)、jad(反编译字节码)、redefine(类热部署)等功能,可在线排查问题。

3.3 类的生命周期

类从加载到卸载的完整过程包括5个阶段:

3.3.1 加载
  • 类加载器根据类的全限定名(如java.lang.String),从文件、网络等渠道获取字节码二进制流。
  • 将字节码信息存入方法区,生成C++实现的InstanceKlass对象(存储类的元信息,不可直接访问)。
  • 在堆中生成对应的java.lang.Class对象(Java层可访问,用于反射操作)。
    • JDK8后,静态字段从方法区移至堆中的Class对象,增强内存管理灵活性。
3.3.2 连接
  • 验证:校验字节码是否符合JVM规范(如魔数、版本号、指令合法性),避免恶意代码执行。
  • 准备:为静态变量分配内存并赋默认值(如int默认0,Object默认null);final静态常量直接赋初始值(如public static final int a=10在此时赋值)。
  • 解析:将常量池中的符号引用(如类名、方法名)替换为内存中的直接引用(地址),建立关联。
3.3.3 初始化
  • 执行静态代码块和静态变量的赋值语句,对应字节码中的clinit方法(由编译器自动生成)。
  • clinit方法的执行顺序与代码编写顺序一致,且仅执行一次(多线程下同步执行,避免重复初始化)。
3.3.4 使用
  • 类的实例化(new对象)、调用静态方法/变量等操作。
3.3.5 卸载
  • 类被垃圾回收器回收,需同时满足:
    1. 该类及子类的所有实例已被回收。
    2. 加载该类的类加载器已被回收。
    3. 该类的Class对象无任何引用。

3.4 类初始化的触发条件

以下情况会触发类的初始化(执行clinit方法):

  1. 访问类的非final静态变量或静态方法(如A.staticFieldA.staticMethod())。
  2. 调用Class.forName("全限定名")主动加载类。
  3. 实例化类(new A())。
  4. 执行该类的main方法。
  5. 子类初始化前,其父类必先初始化(接口除外,父接口初始化不依赖子接口)。
  6. final变量的赋值需要计算(如public static final int a = new Random().nextInt()),而非编译期常量。

3.5 不触发类初始化的情况

  1. 类无静态代码块和静态变量赋值语句(clinit方法不存在)。
  2. 仅声明静态变量但未赋值(如public static int a;,准备阶段已赋默认值)。
  3. final变量为编译期常量(如public static final int a=10,准备阶段已赋值)。
  4. 直接访问父类静态变量(如SubClass.parentStaticField,仅初始化父类,子类不初始化)。
  5. 创建数组(如A[] arr = new A[10],仅初始化数组类,不初始化A类)。

3.6 类加载器的分类

类加载器负责加载类,分为虚拟机底层实现和Java代码实现两类:

JDK8及之前的类加载器
  • 启动类加载器(Bootstrap ClassLoader)
    • 虚拟机底层C++实现,加载JRE/lib下的核心类(如java.lang.String)。
    • 无法通过Java代码获取(Class.getClassLoader()返回null)。
    • 扩展加载路径:通过-Xbootclasspath/a:jar路径添加用户jar包。
  • 扩展类加载器(Extension ClassLoader)
    • Java代码实现(sun.misc.Launcher$ExtClassLoader),加载JRE/lib/ext下的扩展类。
    • 父加载器为null(逻辑上委托给启动类加载器)。
    • 扩展加载路径:通过-Djava.ext.dirs=原目录;用户目录指定(会覆盖默认目录)。
  • 应用程序类加载器(Application ClassLoader)
    • Java代码实现(sun.misc.Launcher$AppClassLoader),加载classpath下的自定义类和第三方jar包。
    • 父加载器为扩展类加载器,是默认的类加载器(Thread.currentThread().getContextClassLoader()默认返回此加载器)。
JDK9及之后的类加载器(模块化)

JDK9引入模块系统后,类加载器结构调整:

  • 启动类加载器:加载核心模块(如java.base)。
  • 平台类加载器(Platform ClassLoader):替代扩展类加载器,加载非核心但重要的平台模块(如java.sql)。
  • 系统类加载器(System ClassLoader):替代应用程序类加载器,加载类路径和模块路径中的类。

3.7 双亲委派机制

类加载器的核心机制,保证类加载的安全性和唯一性。

作用
  1. 安全隔离:防止自定义类替换核心类(如自定义java.lang.String,会被启动类加载器优先加载核心类而失效)。
  2. 避免重复加载:同一类被多个类加载器加载会视为不同类,双亲委派确保类只被加载一次。
机制
  • 类加载器通过parent属性定义委派关系(非继承关系),形成层级:启动类加载器 → 扩展类加载器 → 应用程序类加载器。
  • 加载流程
    1. 自底向上检查:子类加载器先委托父类加载器检查该类是否已加载。
    2. 自顶向下尝试加载:若未加载,父类加载器尝试按自身路径加载;若父类加载失败,子类加载器再尝试加载。
    3. 若所有加载器均无法加载,抛出ClassNotFoundException

3.8 打破双亲委派机制

双亲委派的核心逻辑在ClassLoader.loadClass()方法,打破方式包括:

  1. 自定义类加载器
    • 继承ClassLoader,重写loadClass方法(跳过父类委派逻辑)。
    • 注意:JVM判断两个类是否相同的依据是“全限定名+类加载器”,同一类被不同加载器加载会视为不同类。
  2. 线程上下文类加载器(SPI机制)
    • 线程上下文类加载器默认是应用程序类加载器,允许父类加载器(如启动类加载器)通过它加载子类加载器路径下的类(如JDBC驱动)。
    • 本质是“委托反转”,未完全打破双亲委派,而是补充其跨加载器访问能力。
  3. OSGi框架
    • 支持双向委派(既可以委托父加载器,也可以委托子加载器),实现类的热部署和隔离,适用于模块化应用。

3.9 SPI机制

SPI(Service Provider Interface)是JDK的服务发现机制,允许接口与实现分离。

工作原理
  1. ClassPath/META-INF/services目录下,创建以接口全限定名命名的文件(如java.sql.Driver)。
  2. 文件中写入接口实现类的全限定名(如com.mysql.cj.jdbc.Driver)。
  3. 框架通过ServiceLoader.load(接口类)扫描文件,加载并实例化实现类。
跨加载器访问问题
  • SPI接口(如java.sql.Driver)由启动类加载器加载,而实现类(如MySQL驱动)在应用类路径,需通过线程上下文类加载器获取应用程序类加载器加载实现类:
ClassLoader cl = Thread.currentThread().getContextClassLoader();

3.10 JDBC与双亲委派

JDBC未真正打破双亲委派,而是通过线程上下文类加载器解决跨加载器访问:

  • DriverManager(启动类加载器加载)通过SPI机制发现驱动实现类,再使用线程上下文类加载器(应用程序类加载器)加载驱动。
  • 驱动类加载时仍遵循双亲委派(先委托父加载器),核心规则未被打破。

四、运行时数据区

JVM在运行时管理的内存区域,分为线程私有和线程共享两类,各区域有明确的职责和生命周期。

4.1 线程私有区域(随线程创建/销毁)

4.1.1 程序计数器
  • 作用:存储当前线程执行的字节码指令地址(如分支、循环、异常处理的跳转位置)。
  • 特点
    • 线程私有,确保多线程切换后能恢复执行位置。
    • 唯一不会发生OutOfMemoryError的区域(内存固定,仅存储地址)。
4.1.2 Java虚拟机栈
  • 作用:存储方法调用的栈帧(Stack Frame),遵循“先进后出(FILO)”原则。
  • 栈帧组成
    • 局部变量表:存放方法参数、局部变量、this对象(非静态方法),以“槽”为单位(long/double占2个槽,其他类型占1个)。
    • 操作数栈:临时存储运算数据(如加法运算的操作数),深度在编译期确定。
    • 帧数据
      • 动态链接:将方法的符号引用转换为直接引用(指向运行时常量池)。
      • 方法出口:记录方法返回后的下一条指令地址(供程序计数器使用)。
      • 异常表:记录异常捕获范围及处理指令位置。
  • 参数:通过-Xss设置栈大小(如-Xss256k),默认值依赖操作系统。
  • 异常:栈帧过多(如递归过深)会抛出StackOverflowError;栈扩展失败会抛出OutOfMemoryError
4.1.3 本地方法栈
  • 作用:存储native方法的调用栈帧(与Java虚拟机栈类似)。
  • 实现:HotSpot虚拟机将其与Java虚拟机栈合并,统一管理。

4.2 线程共享区域(随JVM启动/关闭)

4.2.1 堆
  • 作用:存储所有对象实例和数组,是垃圾回收的主要区域。
  • 特点
    • 内存最大的区域,可通过-Xms(初始大小)和-Xmx(最大大小)设置(建议两者相等,减少动态调整开销)。
    • 默认大小:-Xms为系统内存1/64,-Xmx为系统内存1/4。
  • 对象引用:栈的局部变量表中存储堆对象的引用(地址),而非对象本身。
  • 异常:对象分配超过堆最大容量时,抛出OutOfMemoryError
4.2.2 方法区
  • 作用:存储类元信息、运行时常量池、静态变量、JIT编译后的代码等。
  • 实现差异
    • JDK7及之前:以永久代(PermGen Space)实现,位于堆中,大小通过-XX:MaxPermSize设置(易OOM)。
    • JDK8及之后:以元空间(MetaSpace)实现,位于直接内存,大小通过-XX:MaxMetaspaceSize设置(默认无上限,建议显式指定)。
  • 运行时常量池:常量池的运行时表示,存储编译期生成的常量和符号引用,加载后转换为直接引用(内存地址)。
  • 字符串常量池
    • JDK6及之前:属于方法区(永久代),存储字符串副本。
    • JDK7及之后:移至堆中,存储字符串对象的引用。
    • String.intern():将字符串加入常量池,JDK7+返回引用,JDK6返回副本。

4.3 直接内存

  • 定义:不属于JVM规范,是操作系统直接管理的内存(堆外内存)。
  • 引入背景:JDK1.4的NIO为解决堆内存GC影响性能、IO操作数据复制开销大的问题而引入。
  • 分配方式:通过ByteBuffer.allocateDirect(size)分配。
  • 参数-XX:MaxDirectMemorySize设置最大大小(默认与堆-Xmx相同)。
  • 异常:分配超过最大限制时,抛出OutOfMemoryError

4.4 静态变量的存放位置

  • JDK7及之前:位于方法区(永久代)。
  • JDK8及之后:随Class对象存放在堆中,脱离元空间。

4.5 JDK7与JDK8内存结构的区别

区域JDK7及之前JDK8及之后
方法区实现永久代(PermGen Space,堆内)元空间(MetaSpace,直接内存)
静态变量位置永久代堆中的Class对象
字符串常量池永久代(属于运行时常量池)堆中(独立于运行时常量池)
内存参数-XX:MaxPermSize控制永久代大小-XX:MaxMetaspaceSize控制元空间大小

4.6 可能发生内存溢出的区域

  1. Java虚拟机栈:栈帧过多(StackOverflowError)或栈扩展失败(OutOfMemoryError)。
  2. :对象分配超过最大堆容量(OutOfMemoryError: Java heap space)。
  3. 方法区/元空间:类元信息过多(如频繁动态生成类,OutOfMemoryError: Metaspace)。
  4. 直接内存:分配超过最大限制(OutOfMemoryError,错误信息含Direct buffer memory)。

五、垃圾回收机制

垃圾回收(GC)负责回收线程共享区域(堆和方法区)中不再使用的对象,避免内存泄漏。

5.1 回收范围

  • :主要回收不再使用的对象实例。
  • 方法区:回收不再使用的类(需满足类卸载的三个条件,见“类的生命周期-卸载”)。

5.2 对象存活判定算法

5.2.1 引用计数法
  • 原理:为每个对象维护引用计数器,被引用时+1,引用失效时-1,计数器为0则标记为垃圾。
  • 优点:实现简单,判定高效。
  • 缺点:无法解决循环引用问题(如A.ref = BB.ref = A,计数器始终不为0),Java未采用。
5.2.2 可达性分析算法(Java采用)
  • 原理:以“GC Root”为起点,遍历对象引用链,不可达的对象标记为垃圾。
  • GC Root包括
    • 线程栈帧中的局部变量、方法参数。
    • 类的静态变量(由系统类加载器加载的Class对象引用)。
    • 同步锁(synchronized)持有的对象。
    • 本地方法栈中引用的全局对象。

5.3 五种对象引用类型

5.3.1 强引用
  • 最常见的引用(如Object obj = new Object()),只要引用链可达,对象就不会被回收。
  • 即使内存不足,也不会回收强引用对象,会直接抛出OutOfMemoryError
5.3.2 软引用(SoftReference
  • 仅被软引用关联的对象,在内存不足时会被回收(用于缓存场景)。
  • 需配合ReferenceQueue:对象被回收后,软引用会进入队列,可通过遍历队列清除软引用本身。
ReferenceQueue<Object> queue = new ReferenceQueue<>();
SoftReference<Object> softRef = new SoftReference<>(new Object(), queue);
5.3.3 弱引用(WeakReference
  • 无论内存是否充足,只要发生GC就会回收仅被弱引用关联的对象(如WeakHashMap的key)。
  • 同样需通过ReferenceQueue回收弱引用本身。
5.3.4 虚引用(PhantomReference
  • 无法通过虚引用获取对象,唯一作用是在对象被回收时收到通知(用于跟踪直接内存回收)。
  • 必须配合ReferenceQueue使用。
5.3.5 终结器引用(FinalReference
  • 由JVM自动创建,关联待回收的对象,用于在对象回收前执行finalize()方法。
  • 对象被标记为垃圾后,会先进入Finalizer队列,由FinalizerThread执行finalize(),之后才真正回收。

5.4 垃圾回收算法

5.4.1 评价标准
  • 吞吐量:CPU用于执行用户代码的时间占比(吞吐量=用户时间/(用户时间+GC时间))。
  • 堆使用效率:垃圾回收后有效内存的利用率。
  • 最大暂停时间(STW):GC导致用户线程暂停的最长时间(影响用户体验)。
5.4.2 标记-清除算法
  • 流程
    1. 标记:遍历所有对象,标记存活对象。
    2. 清除:回收未标记的垃圾对象,释放内存。
  • 优点:实现简单,无需移动对象。
  • 缺点
    • 内存碎片化严重,可能导致大对象无法分配连续内存。
    • 分配效率低,需遍历空闲链表寻找合适内存块。
5.4.3 复制算法
  • 流程
    1. 将堆分为大小相等的两块(From区和To区),仅使用From区分配对象。
    2. GC时,将From区的存活对象复制到To区,清空From区。
    3. 交换From和To区的角色,重复使用。
  • 优点:无内存碎片,分配效率高(直接指针碰撞)。
  • 缺点:堆利用率低(仅50%),复制大对象成本高。
5.4.4 标记-整理算法
  • 流程
    1. 标记:同标记-清除算法,标记存活对象。
    2. 整理:将存活对象向堆的一端移动,然后清除边界外的垃圾。
  • 优点:无内存碎片,堆利用率100%。
  • 缺点:整理阶段需移动对象,效率较低(需优化移动算法)。

5.5 分代GC算法

结合上述算法的优势,根据对象存活周期划分区域,针对性回收。

5.5.1 分代假说(设计基础)
  • 短命对象假说:绝大多数对象存活时间短(如临时变量)。
  • 弱分代假说:老对象极少引用新对象,跨代引用占比低。
5.5.2 内存分代划分
分代存储对象类型特点
年轻代新创建对象、短生命周期对象空间小(堆的1/3左右)、GC频繁、回收快
老年代长生命周期对象空间大(堆的2/3左右)、GC频率低、回收慢
元空间类元信息、常量等几乎不回收,仅类卸载时触发
  • 年轻代细分
    • Eden区:对象初始分配区域(占年轻代80%)。
    • Survivor区(S0/S1):各占10%,用于存放Eden区GC后存活的对象,始终有一个为空。
5.5.3 回收策略
  1. Minor GC(年轻代回收)
    • 触发:Eden区满时。
    • 算法:复制算法(将Eden+S0的存活对象复制到S1,清空Eden和S0,下次GC交换S0/S1)。
    • 特点:存活对象少,STW时间短(毫秒级)。
  2. Major GC(老年代回收)
    • 触发:老年代空间不足(如年轻代对象存活次数达阈值后进入老年代)。
    • 算法:标记-清除或标记-整理算法(避免复制大对象)。
    • 特点:存活对象多,STW时间长(秒级)。
  3. Full GC(全量回收)
    • 触发:老年代+年轻代均不足、元空间满等极端情况。
    • 行为:同时回收年轻代、老年代和元空间。
    • 影响:STW时间最长,应尽量避免(如通过调整参数减少Full GC频率)。
5.5.4 跨代引用优化(卡表)
  • 问题:Minor GC时若扫描整个老年代检查跨代引用,会降低效率。
  • 解决方案:将老年代划分为“卡页”(默认512字节/页),若老年代对象引用年轻代对象,标记对应卡页为“脏页”。
  • 优化效果:Minor GC仅需扫描“脏页”,大幅减少扫描范围。

5.6 堆内存调整参数

参数作用示例
-Xms堆初始大小(建议与-Xmx相等)-Xms512m
-Xmx堆最大大小-Xmx2g
-Xmn年轻代大小(老年代=堆大小-年轻代)-Xmn1g
-XX:SurvivorRatioEden与S0/S1的比例(默认8:1:1)-XX:SurvivorRatio=8
-XX:+PrintGCDetails打印GC详细日志-
-verbose:gc打印简单GC日志-

5.7 垃圾回收器

不同回收器针对不同场景优化,需根据应用特点选择。

5.7.1 Serial+Serial Old(JDK9废弃)
  • 年轻代(Serial):单线程,复制算法,适用于单CPU环境,STW时间随堆增大变长。
  • 老年代(Serial Old):单线程,标记-整理算法,同上。
  • 参数-XX:+UseSerialGC
5.7.2 ParNew+CMS(JDK14废弃)
  • 年轻代(ParNew):多线程版Serial,复制算法,多CPU下STW时间短。
  • 老年代(CMS):关注STW时间,标记-清除算法,分四阶段:
    1. 初始标记(STW,标记GC Root直接关联对象)。
    2. 并发标记(与用户线程并行,标记所有存活对象)。
    3. 重新标记(STW,修正并发标记的遗漏)。
    4. 并发清理(与用户线程并行,回收垃圾)。
  • 优点:STW时间短,用户体验好。
  • 缺点:内存碎片、浮动垃圾(并发清理时产生的新垃圾)、可能退化为Serial Old。
  • 参数-XX:+UseConcMarkSweepGC(指定老年代用CMS,年轻代默认ParNew)。
5.7.3 Parallel Scavenge+Parallel Old(JDK14废弃)
  • 年轻代(Parallel Scavenge):多线程,复制算法,注重吞吐量,支持自适应调整堆参数(如动态调整年轻代大小)。
  • 老年代(Parallel Old):多线程,标记-整理算法,配合Parallel Scavenge提升吞吐量。
  • 参数-XX:+UseParallelGC(JDK8默认,年轻代Parallel Scavenge+老年代Parallel Old)。
  • 调优参数
    • -XX:GCTimeRatio=n:设置吞吐量目标(n=99表示GC时间不超过1%)。
    • -XX:MaxGCPauseMillis=n:设置最大STW时间(毫秒)。
5.7.4 G1(JDK9默认)

G1(Garbage-First)是面向大堆(多GB)的回收器,兼顾吞吐量和STW时间。

  • 特点
    1. 堆划分为大小相等的Region(1-32MB,2的幂次),每个Region动态承担Eden、Survivor或Old角色。
    2. 优先回收垃圾占比高的Region(“Garbage-First”),最大化回收收益。
    3. 年轻代用复制算法,老年代通过混合回收(Mixed GC)逐步清理。
    4. 支持通过-XX:MaxGCPauseMillis(默认200ms)设置目标STW时间,动态调整回收范围。
    5. 大对象(>Region的50%)直接分配到H-Region,避免频繁晋升。
    6. 无内存碎片(复制算法自然压缩)。
  • 回收流程
    1. 初始标记(STW,标记GC Root直接关联对象)。
    2. 并发标记(与用户线程并行,遍历存活对象)。
    3. 最终标记(STW,修正并发标记的遗漏)。
    4. 筛选回收(STW,按目标时间选择Region回收,复制存活对象)。
  • 参数
    • -XX:+UseG1GC:指定使用G1。
    • -XX:MaxGCPauseMillis=n:设置目标STW时间。
    • -XX:G1HeapRegionSize=n:设置Region大小(1-32MB,2的幂次)。

六、JVM监控与诊断工具

  • Arthas:阿里开源工具,支持GC监控、线程分析、类热部署等,适合在线问题排查。
  • MAT(Eclipse Memory Analyzer Tool):离线分析堆快照,定位内存泄漏(如大对象、内存溢出原因)。
  • JDK自带工具
    • jps:查看Java进程ID。
    • jstat:监控GC统计信息(如jstat -gc 进程ID 间隔时间)。
    • jmap:生成堆快照(jmap -dump:format=b,file=heap.hprof 进程ID)。
    • jstack:查看线程栈信息(定位死锁、阻塞等问题)。
    • hsdb:JDK自带调试工具,查看内存中的对象信息。

文章转载自:

http://G6UY6Xcu.kqgsn.cn
http://8s0rhV2a.kqgsn.cn
http://FncY9Yz3.kqgsn.cn
http://FpH4FNWu.kqgsn.cn
http://UlVeQy7j.kqgsn.cn
http://NKDuJLj1.kqgsn.cn
http://u4ftggcQ.kqgsn.cn
http://xgdTPjEJ.kqgsn.cn
http://g3sSfWdq.kqgsn.cn
http://LRSWoG9L.kqgsn.cn
http://0SlKKPgb.kqgsn.cn
http://rzXLjVFZ.kqgsn.cn
http://ALv0zmN6.kqgsn.cn
http://UFVeglRf.kqgsn.cn
http://ZxcVqKvE.kqgsn.cn
http://3DzjG3RZ.kqgsn.cn
http://EtcsDTrK.kqgsn.cn
http://R1tuhMeg.kqgsn.cn
http://SCnSwMVk.kqgsn.cn
http://wqWf0X02.kqgsn.cn
http://h1xI5LME.kqgsn.cn
http://zGn0wNJ4.kqgsn.cn
http://BDSIXGYl.kqgsn.cn
http://Z0KvoxZU.kqgsn.cn
http://bV14VX7s.kqgsn.cn
http://0caJL6ZN.kqgsn.cn
http://NzQnHAPB.kqgsn.cn
http://7tUp548e.kqgsn.cn
http://iOQYMRFz.kqgsn.cn
http://0KgZ3yMT.kqgsn.cn
http://www.dtcms.com/a/383062.html

相关文章:

  • 驰骋低代码BPM开发平台的组成部分
  • ubuntu22.04源码安装ffmpeg-4.4
  • 黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02
  • 大数据毕业设计选题推荐-基于大数据的教育与职业成功关系可视化分析系统-Spark-Hadoop-Bigdata
  • Ubuntu Server 安装图形界面和通过Window远程桌面连接服务器(Xrdp)
  • 贪心算法在云计算虚拟机部署问题中的应用
  • macOS中找不到钥匙串访问
  • 基于FPGA实现LeNet-5(经典CNN识别手写数字)推理
  • 算法-双指针5.6
  • Eino Indexer 组件完全指南
  • 算法-双指针3.4
  • 【开题答辩全过程】以 “旧书驿站”微信小程序的设计与开发为例,包含答辩的问题和答案
  • Altium Designer使用精通教程 第七章(PCB输出)
  • 【秋招笔试】2025.09.13美团秋招算法岗真题\
  • LeetCode 2367.等差三元组的数目
  • 第16课:多模态Agent协作
  • 《网络攻防技术》第一章: 网络攻防概述
  • 消息语义一致性:Exactly-Once 之外的“效果等价”设计
  • SPI NOR Flash 的命令码详解
  • kafka--基础知识点--5.2--最多一次、至少一次、精确一次
  • Spark(1):不依赖Hadoop搭建Spark环境
  • Python快速入门专业版(三十):函数进阶:函数嵌套与作用域(内部函数访问外部变量)
  • LLaMA-Factory windows wls 安装vllm,并对比速度
  • 全排列问题深度解析:用 Python 玩转 DFS 回溯与迭代
  • 视觉智能的「破壁者」——Transformer如何重塑计算机视觉范式?三大CV算法论文介绍 ViTMAESwin Transformer
  • 语言模型为何会产生幻觉
  • 【Linux指南】Makefile入门:从概念到基础语法
  • 【deepseek】官方API的申请和调用
  • ARM的GIC
  • < 自用文 acme.sh > 使用 Cloudflare API 自动更新证书