JVM成神之路

目录

JVM入门关:

一:JVM的内存布局是咋样的?

二:方法区,永久代,元空间有什么区别?

三:常量池和字符串常量池有什么区别?

四:什么是堆溢出,什么情况下会造成堆溢出

五:什么是栈溢出,什么情况下会造成栈溢出

JVM进阶关:

六:Java是编译型语言还是解释性语言,什么是JIT,什么是热点代码?

七:对象的生命周期包含哪些?

八:什么是双亲委派模型

九:Java有哪些场景是打破了双亲委派机制的

1.SPI机制

2.Tomcat

十:Java有哪些引用类型

JVM成神关:

十一:如何判断一个对象是否存活

十二:常见的垃圾回收算法有哪些?

十三:JVM常见的垃圾回收器有哪些?

十四:CMS垃圾回收器有什么特点

十五:CMS垃圾回收期的执行流程是怎么样的呢?


JVM是Java跨平台实现的基础,也是每个Java开发程序员必备的技能之一,因为在后期的JVM调优方面,好的调优结果可以直接影响项目的执行效率,因此本篇文章就会带大家搞懂JVM的常见问题。

首先,当一个基础的问题,**JVM是如何运行的,**具体的运行流程可以观看我下面画的一张图

基于以上的图片可以得知,JVM执行的流程可以分为下面几步:

1.编译器首先会把 .java 源代码编译为 .class 文件,然年通过类加载器将 .class 文件加载到 运行时数据区域。

2.因为 .class 文件是JVM能识别的一套指令集,操作系统并不认识,因此需要执行引擎来将 .class 文件 进行解释为操作系统可以识别的指令。

3.在执行引擎解释.class文件时,会调用本地方法库的方法来辅助执行。

所以啊,JVM是通过:类加载器,执行引擎,本地方法库,运行时数据区四个模块来执行Java程序的。

明白了JVM是如何运行的,那我们就正式进入JVM的探索之旅啦!!!(搬好小板凳坐好了,准备出发了.....)

JVM入门关:

一:JVM的内存布局是咋样的?

通常所说的内存布局是指运行时数据区,要与Java内存模型分清楚(JMM),运行时数据区是指:堆,虚拟机栈,本地方法栈,方法区,程序计数器五个规范,这是在JVM虚拟机规范所提出来的。

:线程共享的,是JVM中最大的一块区域,存放的是对象的实例

虚拟机栈:线程私有的,存放是方法调用和局部变量(局部变量表,动态链接,操作数栈,方法出口)

本地方法栈:线程私有的,与虚拟机栈类似,存放的是本地方法(c++)

程序计数器:线程私有的,存放的是当前程序进行到了哪一步(当前指令的内存地址)

方法区:线程共享的,存放的是类的结构信息和静态成员变量,常量池等信息。

那什么是Java虚拟机规范呢?

Java虚拟机规范是指各个虚拟机的实现要遵守的一套规则,而Java虚拟机则是规范的具体实现,不同的操作系统都有自己的Java虚拟机实现,因此Java程序在可以一次编写处处可用。

二:方法区,永久代,元空间有什么区别?

方法区是Java虚拟机规范提出的一个专业名词规范,意味着所有的虚拟机实现都会执行该规范,而永久代是Jdk1.7之前的方法区的称呼(默认是使用的HotSpot虚拟机),在1.8之后,用元空间取代了永久代。

为什么要取消永久代呢?

是因为永久代的空间管理很难在满足需求,会出现OOM的问题,因此用元空间取代了永久代,永久代的改动如下:

1.空间大小可以动态调整,这时用的内存空间使用的是本地内存,而不是堆上的内存。

2.字符串常量池从永久代移动到了堆中,减少了方法区GC的压力。

三:常量池和字符串常量池有什么区别?

常量池和字符串常量池都是运行时数据区的一部分,但是二者有以下区别:

1.在jdk1.7之后,将字符串常量池从永久代移动到了堆中,而常量池是存放在元空间中的本地内存中。

2.常量池拥有更多的方法,可以存放字符常量,类,方法,字段的常量,而字符串常量池只可以存放字符串的常量

字符串常量池如下:

四:什么是堆溢出,什么情况下会造成堆溢出

堆溢出是指内存中有大量的垃圾对象无法回收,从而造成堆的内存溢出

常见的堆溢出有以下几种情况:

1.内存泄露:例如使用ThreadLocal时,没有主动释放就会导致内存泄漏。

2.无限创建大量对象

3.没有合理设置堆得大小

4.大量的Execl的导入和导出

五:什么是栈溢出,什么情况下会造成栈溢出

栈溢出通常是指虚拟机栈溢出,而导致虚拟机栈溢出的主要原因是死循环和无限创建大的对象。

例如以下代码,就是一个典型的栈溢出现象,在 main 方法中循环调用 main 方法,循环产生的大量形参都会在栈空间进行创建,当超过栈空间的大小,就会导致栈空间溢出,发生 OOM。

java 复制代码
public class StackOverflowErrorDemo {
    public static void main(String[] args) {
        main(args);   // Exception in thread "main" java.lang.StackOverflowError
    }
}

JVM进阶关:

恭喜你,小伙伴已经走到了JVM的进阶关卡了,下面我们就继续向JVM进发吧!!!

六:Java是编译型语言还是解释性语言,什么是JIT,什么是热点代码?

小伙伴是不是刚看到这个还有点懵,这什么东西,还是连着三个???小伙伴不要着急,我们慢慢来分析解决。

首先

解释型语言:不需要事先编译成机器码,而是在程序运行时将源代码逐行解释执行,例如JS语言就是典型的解释型语言

优点:跨平台性能好,无需编译

缺点:执行效率低

编译型语言:在程序执行前将源代码编译成机器可识别的机器码,然后执行,只需要编译一次,生成的可执行文件可重复运行。

优点:执行效率高

缺点:跨平台能力有限

而Java语言是属于二者之间的,也叫编译-解释型语言

编译阶段: .Java源文件经过编译器编译为 .calss 文件(字节码文件与平台无关,可以在任何系统的虚拟机上执行)

解释阶段:当程序执行时,java虚拟机会加载字节码,并对字节码进行解释执行(JIT:即时编译),JIT会对频繁执行的字节码编译成机器指令用来提高性能。

那热点代码又是啥?其实很简单,在不同的Jdk版本中,对于热点代码的定义是有所不同的,在 JDK 21 Client模式下为1500次,而在JDK 21 Server 模式下为10000次

热点代码的识别基于两种策略:方法调用次数和回边计数。

七:对象的生命周期包含哪些?

对象的生命中周期大致可以分为以下几个阶段

1.加载:根据类的全限定名将其转换为此类的二进制字节流,然后将此二进制流加载到运行时数据区中的方法区,在内存中生成一个这个类的对象,作为类的数据访问入口

2.链接

2.1:验证:验证阶段主要是验证此类中的数据是否合法,例如验证文件格式,字节码,符号的引用等

2.2:准备:为类中定义的静态变量分配内存并设置类的初始值,此时并不会真正赋值,而是赋默认值

2.3:解析:将常量池中的符号引用转换为直接引用的过程,也就是初始化常量。

3.初始化:此时Java虚拟机才开始真正执行类的业务代码,将主导权交给主程序

4.使用:在程序中使用该类

5.销毁:此类的实例没有引用时就会根据垃圾回收算法回收此类的实例。

八:什么是双亲委派模型

所谓的双亲委派模型是指:当一个类加载器收到了类加载的请求,首先类不会自己加载而是将请求委派给父类的加载器,每一次都是如此,最终这个类的加载请求会到达顶级类的类加载器即启动类加载器(BootStrap ClassLoader),当父类无法加载该请求时,子类才会尝试自己记载该请求

使用双亲委派模型的好处:

1.避免类的重复记载

2.更加安全

九:Java有哪些场景是打破了双亲委派机制的

1.SPI机制

SPI机制就是"服务提供发现"机制,例如数据库的驱动就是典型SPI机制,用户调用JDBC接口,而各个数据库厂商都会实现JDBC的驱动,因此是从上往下来实现的

2.Tomcat

一个外置的Tomcat要部署多个应用,多个web应用程序在一个Tomcat容器类运行,而不会造成相互干扰和类冲突,因此Tomcat也打破了双亲委派机制

十:Java有哪些引用类型

在jdk1.2以后Java官方提出了四种引用类型,分别为:强引用,软引用,弱引用,虚引用四种

强引用:一般来说,强引用是最常见的,使用Object object = new Object();都是强引用,除非是显示调用System.gc(),一般垃圾回收器不会主动去回收强引用的对象。类似于公司员工的核心员工一样

软引用:软引用是指一些还有用但不是必须回收的对象,类似于公司员工的好员工。只有在内存实在不够用的时候才会去垃圾回收

弱引用:弱引用是指不管内存是否够用,在下一次GC的时候一定会进行垃圾回收的对象,在Java中典型的运用的就是ThreadLocal中value的引用类型就是弱引用,因此为了防止内存泄露,需要爱使用完ThreadLocal后手动调用remove()方法释放内存,类似于公司员工的一般员工一样。

虚引用:在Java中虚引用又称为幻想引用,不能使用虚引用指向的对象,类似于不合格员工。

JVM成神关:

恭喜小伙伴一路过五关斩六将来到了最后的一部分,JVM成神之路.......

十一:如何判断一个对象是否存活

在Java中利用引用计数法和可达性分析算法来判断一个对象是否存活。

引用计数法:给对象增加一个引用计数器,每当有对象引用时计数器+1,当引用失效时计数器-1,当计数器指向0时,表示该对象已经死亡

优点:实现简单,效率高

缺点:无法解决对象之间的循环引用问题

可达性分析:是指通过一个"GC Roots"的根节点开始逐步向下探索,走过的路径称为引用链,当引用链没有与之直接相连的话,就判断该对象是不可存活的。

那么通常又有哪些对象可以成为GC Roots呢?

1.虚拟机栈中引用的对象

2.本地方法栈中引用的对象

3.方法区中静态变量的引用对象

4.方法区中常量的引用对象

十二:常见的垃圾回收算法有哪些?

在Java中常见的垃圾回收算法有:标记-清除算法,标记-整理算法,复制算法,分代算法等

标记-清除算法:是Java垃圾回收算法中最常见的一种垃圾回收算法,它的核心思想是统一标记可回收的对象,然后统一进行垃圾回收

优点:执行效率比较高,实现简单

缺点:使用标记-清除算法会出现大量的垃圾碎片,如果需要一大片连续的内存空间时候,此种垃圾回收的效率就会打折扣。

标记-整理算法 :标记-整理算法其实是标记-清除算法的一种升级,在统一标记后并不会立即执行垃圾回收,而是将存活的对象移动到另一端,然后清理端外的垃圾对象。

优点:不会存在垃圾碎片的问题

缺点:执行效率比较低下,因为在标记后还需要进行整理存活对象到另一端

复制算法:复制算法是标记-整理算法的一种升级,它的目的就是解决标记-整理算法的效率问题,它将可用内存区域按容量划分为两块大小相等的区域,然后在一块区域进行使用,当需要垃圾回收的时候,会将使用区域的存活对象复制到另一块未使用的区域,然后统一清除使用区域的垃圾对象

优点:执行效率高,不会存在垃圾碎片

缺点:内存空间的利用率比较低

分代算法:分代算法就是将堆内存区域划分为新生代,老生代,然后在各自的区域进行垃圾回收的一种算法

分代算法的大致流程如下:

将堆内存区域划分为新生代,老生代,新生代占比1/3,老生代占比2/3,然后新生代区域又细分为3个区域:end区,To Survivor ,From Survivor占比分别为8:1:1

执行流程大致如下:

1.end区+from Survivor区存活的对象放入 To Survivor区

2.清空end区,from Survivor区

3.交换from Survivor区,To Survivor区

每交换一次存活的对象的年龄+1,当对象的年龄到达15时(HostPot默认是15),就会将该对象放进老生代,当老生代的区域不够用时,就会触发Full GC(全局垃圾回收)。

十三:JVM常见的垃圾回收器有哪些?

常见的垃圾回收器下图所示:在新生代的Serial,ParNew,parallel Scavenge

在老生代的Serial Old,CMS,Parallel Old

已经后续一直在沿用的默认的垃圾回收器G1

在新生代的垃圾回收器常采用的垃圾回收算法是复制算法,在老生代采用的则是标记-整理算法

Serial,Serial Old是单线程环境下的串行执行的,不支持并发操作,意味着在进行垃圾回收时会阻塞用户线程,直到垃圾收集完成。

ParNew 相当于Serial的升级版本,唯一的区别就是ParNew 采用的并行回收,适用于多线程环境下

Parallel Scavenge,Parallel Old是专注于吞吐量的垃圾回收器

(吞吐量 = 用户线程执行时间/总时间 * 100% = 用户线程执行时间/(GC时间+用户线程执行时间)* 100%)

CMS:专注于最短停顿时间的垃圾回收器

十四:CMS垃圾回收器有什么特点

CMS:并发标记清理回收器,是一种获取最短回收停顿时间为目标的垃圾收集器

CMS第一次初始标记,实现用户线程和垃圾回收线程同时执行

,采用的是标记-清除垃圾回收算法,也会存在STW(Stop the word,全局停顿),是一款老生代的垃圾回收器

优点:低延迟,并发收集

缺点:采用标记-清除算法会产生垃圾碎片

十五:CMS垃圾回收期的执行流程是怎么样的呢?

CMS的执行流程如图所示

1.初始标记:只标记GC Roots直接关联的对象,速度很快

2.并发标记:和用户线程同时会执行:GC Roots直接关联的对象继续向下探索,形成一条引用链

3.重新标记:对上一步"并发标记"阶段用户线程还在执行对象的变动进行修正标记

4.并发清除:使用并发-清除算法进行垃圾回收

好了,恭喜小伙伴,已经成功完成了JVM成神之路的所有关卡,与预祝小伙伴在未来的编程生涯中一路畅通,永无BUG!!!

(PS:本文一些图片配图来自于其他网络,若图片作者有看到请联系我删除)

相关推荐
Algorithm15762 分钟前
JVM是什么,与Java的关系是什么,以及JVM怎么实现的跨平台性
java·开发语言·jvm
王佑辉4 分钟前
【jvm】所有的线程都共享堆吗
jvm
琪露诺大湿5 小时前
JavaEE-多线程初阶(1)
java·linux·开发语言·jvm·数据库·java-ee·1024程序员节
威哥爱编程7 小时前
Java Z 垃圾收集器如何彻底改变内存管理
java·jvm·zgc
与海boy10 小时前
第七章 JVM对高效并发的支持
jvm
王佑辉10 小时前
【jvm】如何设置堆内存大小
jvm
MegaDataFlowers20 小时前
JDK、JRE、JVM之间的关系
java·开发语言·jvm
程序员阿鹏20 小时前
详解:单例模式中的饿汉式和懒汉式
java·开发语言·jvm·后端·单例模式·eclipse
梦城忆20 小时前
JVM基础(内存结构)
开发语言·jvm·python
AGi_1 天前
Java基础-JVM
java·开发语言·jvm