JVM知识点总结

本文详细介绍了JVM的类加载过程,包括加载、验证、准备、解析和初始化阶段。类加载器分为启动类加载器、扩展类加载器和应用程序类加载器,遵循双亲委派模型。JVM内存结构包括程序计数器、虚拟机栈、本地方法栈、堆和方法区。讨论了堆与栈的区别,垃圾收集机制如标记清除、复制、标记整理和分代收集,以及各种垃圾收集器的特点。还探讨了JVM8引入元空间的原因和好处,以及解决线上GC频繁问题的策略和G1垃圾收集器的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是类加载?

类加载即将字节码文件读取到内存,存放在运行时数据区的方法区内,并且在堆中创建一个java.lang.Class对象,这个对象封装了方法区的数据结构。类加载的最终产品是堆中的Class对象,提供了访问方法区内的数据结构的接口。

2.类加载的方式?

  • 从本地系统中加载;
  • 网络下载;
  • 从zip,jar归档文件中加载;
  • 从专有数据库加载;
  • 将java源文件编译成.class文件

3.类的生命周期?

加载 连接(验证,准备,解析)初始化

  • 加载:将字节码文件加载进内存,保存在运行时数据区的方法区内,并在堆区创建Class对象;
  • 验证:检查字节流中包含的信息是否符合jvm的要求;
  • 准备:给静态变量分配内存并赋予一个默认初始值;
  • 解析:将类中的符号引用转化为直接引用,直接引用就是直接指向目标的指针、相对偏移量或间接定位到目标的句柄,符号引用就是一组符号来描述目标,可以是任何字面量。
  • 初始化:给静态变量赋予正确的初始值;

4.类加载器?

  • 启动类加载器:负责加载JAVA_HOME/lib路径下或通过-Xbootclass path定义的路径下且被虚拟机认可的类;
  • 扩展类加载器:负责加载JAVA_HOME/lib/ext路径下或通过系统变量java.ext.dir指定的路径下的类;
  • 应用程序类加载器:负责加载用户路径下的类;
  • 用户自定义类加载器:用户可以通过继承java.lang.ClassLoader来实现自定义类加载器。

5.JVM类加载机制?

  • 全盘负责:
  • 父类委托:
  • 缓存:

6.双亲委派模型?

一个加载器接收到类加载请求之后,首先本身先不进行加载,而是将请求委托给父类进行加载,依次向上,因此所有的类加载都会传到启动类加载器那里,只有当父类加载器无法加载时(类路径下找不到对应的类),子类加载器才会自己去加载该类。
双亲委派模型的好处:

  • 每一个类都只会被加载一次,避免了重复加载;
  • 每一个类都会被尽可能的加载(从引导类加载器往下,每个加载器都可能会根据优先次序尝试加载它);
  • 有效避免了某些恶意类的加载(比如自定义了Java.lang.Object类,一般而言在双亲委派模型下会加载系统的Object类而不是自定义的Object类)

7.jvm的组成?

jvm有两个子系统,两个组件;
两个子系统为:类加载器,执行引擎
两个组件为:运行时数据区,本地接口库

8.jvm内存结构

  1. 程序计数器:指的是执行的字节码文件的行号指示器,线程私有,是内存中唯一一个没有oom的区域;
  2. Java虚拟机栈:用于描述方法执行时的内存结构,当执行一个方法的时候,都会创建一个帧栈,用于存储局部变量表,操作数栈,动态链接,方法入口等。一个方法的执行到结束,对应着帧栈的入栈和出栈;也是线程私有的。
  3. 本地方法栈:和java虚拟机栈类似,只是服务的对象是native方法;
  4. 堆:用于给类的对象和数组分配内存的区域,是线程共享的。也是GC的主要区域,从GC的角度还可以分为新生代和老年代。
  5. 方法区:又称为老年代,用于存储被jvm加载的类信息,静态变量,常量,即使编译器编译后的代码。这一部分的垃圾回收主要在于常量池的回收和类型的卸载。

9.堆和栈的区别?

  • 物理地址:栈是连续的,因此效率会比较高;堆是不连续的,效率低;
  • 内存分别:堆不连续,所以分配的内存在运行期确认,因此大小不固定;而栈连续,内存在编译期间就确认,因此大小确定。
  • 存放的内容:堆存放的是对象实例,数组,更关注数据的存储;而栈存储的是局部变量,操作数栈,返回结果等,更关注方法的执行。
  • 程序的可见度:堆对于整个应用程序都是共享可见的,而栈只是对于线程可见,所以也是线程私有的,生命周期和线程相同。

10.如何确定垃圾?

  • 引用计数法
    如果要操作一个对象必须要有引用,一个简单的办法就是通过引用计数判断一个对象是否可以回收

  • 可达性分析

    如果在GC-Roots和一个对象之间没有可达路径,则称对象是不可达的,但是不可达对象不等价于客户收对象,不可达对象变为可回收对象至少要经过两次标记。

11.垃圾清除算法

  • 标记清除法:
  • 复制算法
  • 标记整理算法
  • 分代收集算法

12.垃圾收集器

  1. Serial垃圾收集器
  2. ParNew垃圾收集器
  3. Parallel Scavenge收集器
  4. Serial Old收集器
  5. Parallel Old收集器
  6. CMS收集器
  7. G1收集器

13.Java中的四种引用类型

  • 强引用
  • 软引用
  • 弱引用
  • 虚引用

14.JVM8为什么要增加元空间,带来什么好处?

原因:

  1. 字符串在永久代中容易出现内存溢出和性能问题;
  2. 类及方法的信息等难以确定大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大容易造成老年代溢出;
  3. 永久代会为GC带来不必要的复杂度,回收效率比较低;

好处:

  1. 每个加载器有专门的空间;
  2. 不会单独回收某个类;
  3. 元空间里的对象的位置是固定的;
  4. 如果发现某个类加载器不再存活了,会把相关的空间整个回收。

15.如何解决线上gc频繁的问题?

  1. 查看监控,以了解出现问题的时间点以及当前FGC的频率(可对比正常情况看频率是否正常)
  2. 了解该时间点之前有没有程序上线、基础组件升级等情况。
  3. 了解JVM的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃 圾收集器,然后分析JVM参数设置是否合理。
  4. 再对步骤1中列出的可能原因做排除法,其中元空间被打满、内存泄漏、代码显式调用gc方法 比较容易排查。
  5. 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。
  6. 通过可疑对象定位到具体代码再次分析,这时候要结合GC原理和JVM参数设置,弄清楚可疑 对象是否满足了进入到老年代的条件才能下结论。

16.堆G1垃圾收集器有了解么,有什么特点

  1. G1的设计原则是"首先收集尽可能多的垃圾(Garbage First)"。因此,G1并不会等内存耗尽(串行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;
  2. G1采用内存分区(Region)的思路,将内存划分为一个个相等大小的内存分区,回收时则以分区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位进行操作,因此G1天然就是一种压缩方案(局部压缩);
  3. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换;
  4. G1的收集都是STW的,但年轻代和老年代的收集界限比较模糊,采用了混合(mixed)收集的方式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。
  5. 因为G1建立可预测的停顿时间模型,所以每一次的垃圾回收时间都可控,那么对于大堆(16G左右)的垃圾收集会有明显优势.
<think>好的,我现在需要回答用户关于JVM常用知识点总结的问题。根据用户提供的引用内容,我需要整理JVM的主要知识点,包括类加载、内存结构、垃圾回收和调优等方面。同时,用户还要求生成相关问题,所以我要确保覆盖这些方面的内容,并基于引用中的信息进行回答。 首先,我需要回顾用户提供的四个引用内容。引用[1]提到运行时数据区域,即JVM的内存结构。引用[4]详细列出了类加载、内存结构、垃圾回收和调优的各个子项,这可以作为回答的主要结构。引用[2]和[3]则涉及垃圾回收器的参数设置和工具使用,这部分需要整合到垃圾回收和调优部分。 接下来,我需要按照引用[4]的结构来组织回答:类加载篇、JVM内存结构篇、垃圾回收篇、JVM调优篇。每个部分下再分小点,比如类加载过程、类加载器分类、双亲委派机制等。同时,要确保使用正确的中文表达,并遵循系统级指令中的格式要求,例如数学表达式用$...$,独立公式用$$...$$,但根据用户的问题,可能数学公式较少,更多是技术术语。 在引用其他内容时,需要自然地添加引用标识,比如在提到内存结构时引用[1],在讨论堆内存溢出自动dump时引用[2],在说明GC日志和工具时引用[3],在整体结构上引用[4]。 然后,生成相关问题需要考虑用户可能感兴趣的后续问题,比如类加载机制、双亲委派的好处、内存结构各部分的作用、垃圾回收算法比较、调优工具使用等。需要确保问题覆盖用户可能想深入了解的方面,并基于已有的引用内容,避免超出范围。 最后,检查是否符合所有系统级指令:行内数学表达式是否正确(虽然这里可能用不上),独立公式是否单独成段(可能同样不适用),LaTeX语法正确,使用中文回答,生成相关问题,并在引用处标注标识符。同时,用户可见层要求结构清晰,帮助用户逐步解决问题,回答真实可靠。 总结步骤: 1. 按照引用[4]的结构组织内容,分为四个主要部分。 2. 每个部分下详细列出子项,结合其他引用的具体信息。 3. 在适当的位置添加引用标识,如内存结构引用[1],调优参数引用[2][3]。 4. 生成相关问题,覆盖各个主要部分的关键点。 5. 确保回答符合格式要求,无Markdown,使用中文,结构清晰。 现在开始撰写正式回答,确保每个部分简明扼要,信息准确,引用正确。</think>### JVM 常用知识点总结 #### 一、类加载篇 1. **类加载过程** 类加载分为 **加载、验证、准备、解析、初始化** 五个阶段。 - **加载**:通过类名获取二进制字节流,生成类的Class对象。 - **验证**:确保字节码符合JVM规范,例如文件格式、元数据、字节码验证[^4]。 - **准备**:为类变量分配内存并设置初始值(零值)。 - **解析**:将符号引用转换为直接引用。 - **初始化**:执行类构造器`<clinit>()`方法,完成静态变量赋值和静态代码块执行。 2. **类加载器分类** - **启动类加载器(Bootstrap ClassLoader)**:加载`JRE/lib`下的核心类库(如`rt.jar`)。 - **扩展类加载器(Extension ClassLoader)**:加载`JRE/lib/ext`目录的类。 - **应用程序类加载器(Application ClassLoader)**:加载用户类路径(ClassPath)的类。 - **自定义类加载器**:用户可通过继承`ClassLoader`实现自定义加载逻辑[^4]。 3. **双亲委派机制** 类加载时优先委派给父类加载器,若父类无法加载,子类才尝试加载。 **优点**:避免重复加载,保证核心类库的安全性。 --- #### 二、JVM内存结构篇 1. **JVM内存结构** JVM内存分为以下区域: - **堆(Heap)**:存储对象实例,是垃圾回收的主要区域[^1]。 - **方法区(Method Area)**:存储类信息、常量、静态变量。 - **虚拟机栈(VM Stack)**:存储方法调用的栈帧(局部变量表、操作数栈等)。 - **本地方法栈(Native Method Stack)**:为Native方法服务。 - **程序计数器(PC Register)**:记录当前线程执行的位置。 2. **对象的创建过程** 1. 类加载检查 → 2. 分配内存(指针碰撞或空闲列表) → 3. 初始化零值 → 4. 设置对象头 → 5. 执行`<init>`方法。 3. **强软弱虚引用** - **强引用**:对象存活时不会被回收。 - **软引用**:内存不足时可能被回收。 - **弱引用**:垃圾回收时立即被回收。 - **虚引用**:用于跟踪对象被回收的状态。 --- #### 三、垃圾回收篇 1. **如何判断对象是否死亡** - **可达性分析**:从GC Roots出发,不可达的对象标记为可回收。 - **引用计数法**(较少使用):循环引用可能导致内存泄漏。 2. **垃圾回收算法** - **标记-清除**:产生内存碎片。 - **复制算法**:适用于新生代(如Survivor区)。 - **标记-整理**:适用于老年代。 - **分代收集**:结合新生代(Minor GC)和老年代(Full GC)特性优化。 3. **常见垃圾收集器** - **Serial/Serial Old**:单线程,适合客户端应用。 - **ParNew**:多线程版Serial。 - **CMS(Concurrent Mark Sweep)**:以最短停顿时间为目标,但会产生内存碎片。 - **G1(Garbage-First)**:分Region回收,兼顾吞吐量和低延迟。 --- #### 四、JVM调优篇 1. **调优时机** - 频繁Full GC或长时间GC停顿。 - 堆内存溢出(可通过`-XX:+HeapDumpOnOutOfMemoryError`生成dump文件分析[^2])。 2. **监控与诊断工具** - **jps**:查看Java进程ID[^3]。 - **jstat**:监控GC状态(如`jstat -gc <pid>`)。 - **jstack**:生成线程快照,排查死锁。 - **jmap**:分析堆内存对象分布(如`jmap -histo <pid>`)。 3. **常用调优参数** - 堆大小:`-Xms`(初始堆)、`-Xmx`(最大堆)。 - 新生代比例:`-XX:NewRatio`。 - GC日志:`-XX:+PrintGCDetails -XX:+PrintGCTimeStamps`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值