Java面试核心知识点——第一章JVM

1.1 JVM的运行机制

什么是JVM?
JVM是Java Virtual Machine,Java虚拟机。

Java程序的具体运行过程:
1)java源文件被编译成class字节码文件
2)JVM将class字节码文件转为机器码
3)机器码调用操作系统本地方法库执行方法

JVM的具体组成和交互图
在这里插入图片描述

1.2 JVM线程及多线程

JVM准备工作完成后会调用操作系统创建一个对应的原生线程
原生线程初始化后,调用run方法就开始执行了
在这里插入图片描述

1.3 JVM的内存区域

JVM的内存区域主要分三大类:
1)线程私有区域
2)线程共享区域
3)直接内存/堆外内存

关于堆外内存的例子:堆外内存并不属于JVM的内存,在并发编程中常用;
如NIO模块提供的Channel与Buffer的 I/O 操作方式就是基于堆外内存
具体过程:
1)NIO模块调用Native函数库,直接在操作系统上开辟堆外内存
2)使用DirectByteBuffer对象引用这块内存实现对这块内存的管理
3)这样可防止堆中内存复制数据带来的性能消耗和资源浪费

1.3.1 程序计数器 线程私有、无内存溢出的唯一区域

1)一块很小的内存空间
2)用来记录当前运行的class字节码的行号
3)不记录本地方法库的执行 => Undefined
4)每个线程都有一个独立的计数器

1.3.2 虚拟机栈 线程私有、记录方法的执行

1)程序方法执行 => 栈帧进栈
2)程序方法退出/异常 => 栈帧弹栈
3)每个线程中只有一个栈帧处于活跃状态(peek)

在这里插入图片描述

1.3.3 本地方法区,线程私有

和栈类似,主要区别:
1)栈 服务 字节码的方法
2)本地方法区 服务 Native方法

1.3.4 堆,运行时数据区、线程共享

所有的运行时数据都在堆中,
运行时创建的对象,运行时产生的数据

JVM采用分代收集算法对堆进行垃圾回收
所以堆区的细分:
1)新生代1/3
2)老年代2/3
3)永久代

1.3.5 方法区,线程共享

方法区就是永久代,存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量池等数据
永久代的内存回收主要针对 常量池的回收 和 类的卸载
可回收对象很少
在这里插入图片描述

1.4 JVM的运行时内存

1.4.1 新生代:Eden区、SurvivorFrom区、SurvivorTo区

Eden区8/10:存放新创建的对象(大对象除外),内存不足触发MinorGC
SurvivorFrom区1/10:作为MinorGC的扫描对象
SurvivorTo区1/10:保留来自上一次MinorGC的幸存对象

新生代GC为MinorGC,采用复制算法,具体过程:
1)将Eden和SurvivorFrom中存活的对象复制到SurvivorTo中
如果对象周期达15(老年代)标准,或者对象过大,则复制到老年代中且年龄+1
2)清空Eden区和SurvivorFrom区
3)交换SurvivorFrom区和SurvivorTo区

1.4.2 老生代

老生代:存放大对象和长周期对象,在内存不足时触发MajorGC
在执行MajorGC前会先执行一次MinorGC

老生代GC为MajorGC,采用标记清除算法,具体过程:
1)标记阶段:标记老生代中所有存活的对象
2)清除阶段:清除那些未标记的对象

由于采用标记清除算法,老生代中容易出现内存碎片化问题,容易出现Out Of Memory问题

1.4.3 永久代

永久代:内存中永久保存的区域,主要存放Class和Meta(元数据)信息

Class在类加载时被放入永久代
不对永久代触发GC

容易导致加载类过多导致Out Of Memory问题
例如:一次性加载过多的Jar包 的Tomcat因为内存不足无法启动

注意:在Java8后,永久代改为了元空间,直接使用操作系统的内存,不受JVM内存限制。

1.5 垃圾回收与算法

1.5.1 如何确定垃圾

1)引用计数法:容易引起循环引用问题
2)可达性分析

引用计数法

1)添加对该对象的引用,引用计数+1;
2)删除对该对象的引用,引用计数-1;
3)认为引用计数==0的对象可以被回收
循环引用问题:如果两个对象互相引用,则永远无法回收

可达性分析

1)定义一些GC Roots对象
2)向下搜索,如果到某个对象完全没有可达路径,做一次标记
3)标记两次后还不可达,就对该对象进行回收

1.5.2 Java中常用的垃圾回收算法

1)标记清除算法

标记阶段:标记存活对象
清除阶段:清除未标记对象
缺点:容易使得内存碎片化

2)复制算法

复制阶段:将存活对象复制到另一区域
清除阶段:清除前一区域的所有对象
优点:无碎片化问题
缺点:需要额外开辟一个区域,浪费空间

3)标记整理算法

结合标记清除和复制算法的优点
标记阶段:标记存活对象
复制阶段:将存活对象复制到另一区域
清除阶段:清除未标记的对象

4)分代回收算法

根据对象种类不同,采用不同的算法进行GC
1)新生代采用MinorGC:复制算法
2)老生代采用MajorGC:标记清除算法

1.6 Java中的4中引用类型

1)强引用
对象赋值
2)软引用
通过SoftReference类实现,在空间不足时回收
3)弱引用
通过WeakReference类实现,每次GC都会回收
4)虚引用
通过PhantomReference类实现,和引用队列配合使用,确认回收状态

1.7分代收集算法和分区收集算法

1.7.1 分代收集算法

1.新生代主要存储短生命周期的对象,因此在垃圾回收的标记阶段会标记大量已死亡对象以及少量的存活对象,因此只需选用复制算法将少量存活的对象复制到内存的另一短并清理原区域的内存即可。
2.老年代主要存放长生命周期的对象和大对象,可回收的对象一般较少,因此JVM采用标记整理算法进行垃圾回收,直接释放死亡状态的对象所占用的内存空间。

1.7.2 分区收集算法

分区收集算法是将整个堆空间划分为【连续】的【大小不同】的区域,在每个小区域内单独进行内存使用和垃圾回收
好处:灵活使用每个小区域内存,和灵活释放内存

并且分区收集算法可以根据系统可接受的停顿时间,每次都快速回收若干个小区域中的内存,以缩短时系统停顿的时间,最后以多次并行累加的方式逐步完成整个内存区域的垃圾回收。如果垃圾回收机制一次回收整个堆内存,则需要更长的系统停顿时间,长时间的系统停顿将影响系统运行的稳定性。

1.8 垃圾收集器

JVM针对新生代和老年代有不同的垃圾收集器。
新生代:Serial、ParNew、Parallel Scavenge
老年代:CMS、Serial Old、Parallel Old
针对不同区域:G1

在这里插入图片描述

1.10 JVM的类加载机制

JVM的类加载分为5个阶段:1加载、2验证、3准备、4解析、5初始化

1.10.1 JVM的类加载阶段

1 加载

JVM读取Class文件,根据Class文件创建对象过程
1)将Class文件读取到运行时的方法区内
2)在堆区中创建对象
3)封装类在方法区的数据结构过程
可通过jar、war包读取,也可通过代理自动生成Class或其他方式读取

2 验证

验证Class文件是否符合要求

3 准备

1)在方法区中为 类变量(static) 分配内存空间并设置初始值(default)
2)为类常量(static final)赋值

4 解析

JVM将常量池中的符号引用替换为直接引用

5 初始化

执行类构造器的< clinit >方法为类进行初始化
< clinit > 是在编译阶段由编译器自动收集类中静态语句块和变量的赋值操作组成

先执行父类的< clinit >方法才执行子类的 < clinit > 方法
如果一个类没有静态语句块,也没有静态变量赋值操作,就不生成该方法

以下情况JVM不会执行类的初始化:
1)常量,在编译时会将其存入该常量的类的常量池中,不需要调用常量所在的类,不会触发该常量类的初始化
2)子类引用父类的静态字段 ,不会触发子类的初始化,但会触发父类的初始化
3)定义对象数组,不会触发该类的初始化
4)用类名获取Class对象,不会触发
5)使用Class.forName加载指定类,通过initialize参数设置是否需要初始化
6)使用ClassLoader默认的loadClass方法加载类时不会触发该类的初始化

1.10.2 类加载器

三种类加载器:
1)启动类加载器:负责加载JAVA_HOME/lib目录中的类库
![在这里插入图片描述](https://img-blog.csdnimg.cn/203df2b43a4e4904bff6aa8ed33abac2.png在这里插入图片描述7

该目录下主要有

2)扩展类加载器:负责负责加载JAVA_HOME/lib/ext 目录中的类库
在这里插入图片描述

3)应用程序类加载器:负责加载用户路径(classpath)上的类库

我们可以通过继承 java.lang.ClassLoader实现自定义的类加载器

4)JDK所有jar包
在这里插入图片描述
我们来看看这个rt.jar是什么?

路径:JAVA_HOME/jre/lib/rt.jar
在这里插入图片描述
路径:JAVA_HOME/jre/lib/rt.jar/java
在这里插入图片描述
所以我们所有用到的类,包括java.lang.String,java.lang.Object都是由启动类加载器加载,这都是钉死的
保证了类加载的唯一性。

1.10.3 双亲委派机制

JVM通过双亲委派机制对类进行加载
双亲委派机制:
1)一个类收到类加载请求后不会自己加载这个类,而是把该类的加载请求向上委派给其父类去完成,
2)其父类在接收到该类加载请求后又会将其委派给自己的父类
3)所有的类加载请求都被向上委派到启动类加载器中
4)如果父类加载器发现自己无法加载该类(原因是该类的Class文件在父类的类加载路径中不存在),就把信息反馈给子类并向下委派子类加载器加载该类,直到加载成功
5)若找不到该类,JVM抛出ClassNotFound异常

双亲委派机制的核心就是保障类的唯一性和安全性
如果在JVM存在包名和类名相同的两个类,则该类无法加载,JVM也无法完成类加载流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值