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

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。

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:本文一些图片配图来自于其他网络,若图片作者有看到请联系我删除)


http://www.kler.cn/news/364570.html

相关文章:

  • Java 代理模式详解
  • GPIO输入和输出
  • 【js逆向专题】12.RPC技术
  • 【Java】使用iText依赖生成PDF文件
  • html 轮播图效果
  • 大数据生态守护:Hadoop的深度保护策略
  • 测试学习-测试分类
  • Spring Boot框架中小企业设备监控系统开发
  • c++中虚函数表属于类还是属于对象?
  • Ubuntu20.04 更新Nvidia驱动 + 安装CUDA12.1 + cudnn8.9.7
  • 【数据结构与算法】力扣 46. 全排列
  • 中国人寿财险青岛市分公司引领科技金融新风尚
  • HarmonyOS 5.0应用开发——应用打包HAP、HAR、HSP
  • 新160个crackme - 082-phox.1
  • Elasticsearch在分布式集群中进行数据分片的策略能否完全避免数据热点?数据分片分布不均会导致性能瓶颈吗?如何通过实践优化分片分布?
  • 本地生活平台开发搭建方案 同城O2O电商平台推广运营
  • OpenCV视觉分析之运动分析(2)背景减除类:BackgroundSubtractorKNN的使用
  • 缓存雪崩是什么
  • SparseRCNN 模型,用于目标检测任务
  • 云计算行业应用实训室建设方案
  • es 常用命令(已亲测)
  • Unity编辑器 连接不到SteamVR问题记录
  • 一个批量输出PDF页数的python程序
  • 常用MQ组件选型时需要考虑的问题
  • 独家大模型经典面试秘籍:问题答案超详细,收藏此文就够咯
  • AnaTraf | 探讨TCP握手时延