学过Java的小伙伴肯定对JVM(Java虚拟机)多多少少了解一点,Java的“一次编译,到处运行”的特点就离不开他。今天我们就通过面试题去进一步的了解它。🌈
JVM内存区粗略的可以划分为堆和栈,按照虚拟机规范,又可分为一下几个区域:
JVM内存分为线程私有区和线程公共区。其中,堆内存和方法区属于线程公共区,虚拟机栈、本地方法区和程序计数器属于线程私有区,每个区的组成如上图所示。
我们大致可以分为六个过程:
类加载:首先,JVM会从类路径中加载所需要创建的对象类,如果该类没有被加载,JVM会根据类的全限定名找到对应的字节码文件,并加载到内存里面去。
🧑💼面试官追问:JVM创建对象时,堆会发生抢占吗?
有可能会。
🧑💼面试官继续追问:如何解决堆抢占这个问题呢?
- 引用计数法:该方法通过对对象进行引用计数,每次对象时计数加1,当引用计数为0时,表示没有被引用,可以被回收。但是引用技术法无法解决循序引用的问题,导致内存泄漏。
- 可达性分析法:常见的垃圾回收算法中,主要采用的可达性分析算法。该算法从一组成为“根”的特定对象(如全局变量、活动线程等)开始,通过追踪对象之间的引用关系形成引用图,然后检查哪些对象可以从“根”对象访问到。如果一个对象可以从“根”对象访问到,那么它就会被认为是存活的。而对于无法从“根”对象访问到的对象,则被判断为不在存活,垃圾回收器将其进行回收。
🧑💼面试官追问:垃圾算法了解吗?
- 标记-清除算法:该算法通过标记对象的可达性来确定对象存活,清除未标记的对象。这种算法虽然零活,但是可能会产生内存碎片。
- 标记-复制算法:该算法需要额外的内存区,将正在使用的内存区里面存活的对象进行标记,然后复制到新的缓存区里面。缺点:需要额外的内存区来保存复制对象,与其他算法比,有一定的内存浪费。
- 标记-整理算法:该算法首先标记存活的对象,然后将他们紧凑的排列到内存区的一端,清除未标记的对象。缺点:需要进行对象移动的操作,可能增加垃圾回收的时间消耗。
- 分代回收算法:该算法根据对象的生命周期将内存分为不同的区。新创建的对象会分到新生代,经过多次回收仍然存活的对象会晋升到老年代。不同带使用不同的垃圾回收策略,以提高效率。
- 新生代:每次都有大量对象消亡,因为有老年代作为内存担保,通常采用复制算法。
- 老年代:对象存活时间长,可采用标记清除、标记整理算法。
CMS(Concurrent Mark Sweep)收集器:是一种以获取最短回收停顿时间为目标的收集器。它主要针对响应时间敏感的应用程序,并通过执行大部分垃圾收集工作来减少停顿时间。
CMS收集器的垃圾收集过程主要分为五个阶段:
收集流程:
G1垃圾收集器的设计目标是在停顿时间可控的情况下,最大化系统吞吐量,它旨在提供更可控、更高效的垃圾回收性能。
- 尽量不要使用
static
成员变量,减少生命周期;- 及时关闭无用的资源
- 不用的对象,可以手动设置为 null
虚拟机把描述类的数据从Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
- 加载:查找并加载类的二进制数据
- 验证:确保被加载的类的正确性
- 准备:为类的静态变量分配内存,并将其初始化为默认值
- 解析:把类中的符号引用转换为直接引用
- 初始化:为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化