Java面试基础篇之JDK和JRE区别、字节码优势和GC可达性分析

前言

在面试中,JDK和JRE的区别字节码的优势GC垃圾回收器,这些都是会被经常问到的问题,那么你对这些问题有多少了解呢,能答出来多少呢,如果不能快速答出,或者没有明确的答案,那么在看完本篇博客后,相信你会有所收获!

1. JDK、JRE和JVM三者区别和联系

1.1 JDK、JRE和JVM三者区别
1.1.1 JDK是什么?

JDK:全称是Java Development Kit,指的是Java的开发工具

1.1.2 JRE是什么?

JRE:全称是Java Runtime Environment,也就是Java运行时环境

1.1.3 JVM是什么?

JVM:全称是Java Virtual Machine,是指Java虚拟机

1.2 JDK、JRE和JVM三者联系

在这里插入图片描述

体现Java的一大特性:支持跨平台即一次编译,到处运行

2. 什么字节码?采用字节码的好处是什么?

2.1 Java中的编译器和解释器
  • Java中引用了虚拟机(JVM),即机器与编译程序之间加入了一层抽象的虚拟的机器,这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口

无论是Windows系统还是Linux系统上,
比如有一个App程序,把程序编译成一个Jar包或者War包
这是由于JVM在中间进行了处理,
向任何平台都提供了一个程序编译的共同接口,
因此,在Windows上的程序也能在Linux上运行

  • 编译程序只需要面向Java虚拟机,生成虚拟机能够理解的代码,然后由虚拟机代码转换成特定的机器码执行

  • 在Java中,这种共虚拟机理解的代码就叫做字节码 (即拓展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机

  • 每一种平台的解释器是不同的,但是实现的虚拟机是相同的

Java源程序经过编译器编译后变成字节码;
字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器;
解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。

这也就是解释了Java的编译与解释并存的特点

Java代码的执行过程

Java源代码 —> 编译器 ----> JVM可执行的Java字节码(即虚拟指令) —> JVM —> JVM中解释器 —> 机器可执行的二进制机器码 —> 程序运行

2.2 采用字节码的好处是什么?
  • Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率的问题

传统的解释性语言是编译一句,解释一句

而Java是将编译和解释拆分开:编译是提前做的,在IDE中编写完后就自动编译成.class文件

传统的编译性语言是编译一句,解释一句,解释也会也占用运行时间
而而Java是将编译和解释拆分开,因此执行效率就会相对较高

  • 同时又保留了解释性语言的可移植的特点
  • 所以Java程序运行时比较高效,而且由于字节码并不专门针对一种特定的机器
  • 因此,Java程序无需重新编译便可在不同计算机上运行

3. GC如何判断对象可以被回收?

3.1 引用计数法
1.什么是引用计数法

引用计数法每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时减1,计数为0时可以回收 (但是Java的GC并没有采用引用计数法来判断对象是否可以回收,并且GC并不是Java所独有的,像python和Go语言的GC采用的就是这种方法)

2.为什么Java中GC没有采用计数引用法?

因为引用计数法存在一个很大的缺陷,例如可能会出现A引用了B,B又引用了A,这时候就算它们都不再使用了,但因为相互所以其引用计数器=1,永远永远无法回收

3.为什么Python还采用引用计数法呢?

虽然说引用计数法回存在这种永远无法被回收的缺陷,但其执行效率还是很高的,比如新增一个引用就加1,引用释放就减1,不会消耗太多的性能

3.2 可达性分析
1.什么是可达性分析?

可达性分析法从GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots 没有任何引用链相连时,则证明此对象是不可用的,所以虚拟机就判断是可回收对象 (Java中GC采用的就是这种对象回收方式)

可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会

2.为何不可达对象可以进行自我拯救?

因为对象被系统宣告死亡至少要经历两次标记过程

  • 第一次是经过可达性分析发现没有域GC Roots相连的引用链
  • 第二次是在由虚拟机自动建立的finalize队列中判断是否需要执行finalize()方法

第二次标记具体过程

  • 当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,
    若未覆盖,则直接将其回收

  • 否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法

  • 执行finalize方法完毕后,GC会在此判断该对象是否可达,若不可达,则进行回收;否则,对象“复活” (如果在finalize方法中引用了其他对象,就会判断其可达,然后进行"复活")

注意

  • 每个对象只能触发一次finalize()方法
  • 由于finalize()方法运行代价昂贵,不确定性大,无法保证各个对象的调用顺序,不推荐使用它,建议遗忘它
3. GC Roots的对象有哪些?
  • 虚拟机栈 (栈帧中的本地变量) 中引用的对象

    例如存在一个add的静态方法,其中引用了了User对象

public static User add(int id) { //静态方法add
  ......
  //User对象就是一个GC Roots,如果其他对象被它引用,那么它就就不能被回收
  return new User();
}

方法执行完后局部变量都会被回收,但User对象是一个GC Roots,那么它是怎样被GC进行回收的呢?

必须要线程执行到add方法时(即add方法还在活动栈中),执行完后该方法出栈;GC在进行对象回收时,只会遍历活动栈中的内容 (即还在栈中的对象),把它们作为GC Roots对象进行可达性分析;由于add方法执行完后被弹出栈外,就不再进行可达性分析了,自然就就被GC回收了

  • 方法区中类静态属性引用的对象

    被static修饰符修饰

  • 方法区中常量引用的对象

    也是被static修饰符修饰

  • 本地方法栈中的JNI (即一般说的Native方法) 引用的对象

    native方法一般是由JVM自动去调用的

好了,今天有关JDK、JRE和JVM的区别字节码的好处GC的可达性分析的学习就到此结束了,欢迎大家学习和讨论!

参考视频链接
https://www.bilibili.com/video/BV1Eb4y1R7zd (B站UP主程序员Mokey的Java面试100道)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值