安卓面试必备的JVM虚拟机制详解,看完之后简历上多一个技能( 三 )


Java 中的引用可以分为四类 , 强引用、软引用、弱引用和虚引用 。 强引用在程序中普遍存在 , 类似 new 的这种操作 , 只要有强引用存在 , 即使 OOM JVM 也不会回收该对象 。 软引用是在内存不够用时 , 才会去回收 , JDK 提供了 SoftReference 类来实现软引用 。 弱引用是在 GC 时不管内存够不够用都会去回收的 , 可以使用 WeakReference 类来实现弱引用 。 虚引用对对象的生命周期没有影响 , 只是为了能在对象回收时收到一个系统通知 , 可以使用 PhantomReference 类来实现虚引用 。
接下来就是要讲垃圾回收算法了 。
垃圾回收算法垃圾回收算法主要有标记清除、复制算法、标记整理 。 标记清除是先通过 GC Roots 标记所存活的对象 , 然后再统一清除未被标记的对象 , 它的主要问题是会产生内存碎片 。 老年代使用的 CMS 收集器就是基于标记清除算法 。 复制算法是把内存空间划分为两块 , 每次分配对象只在一块内存上进行分配 , 在这一块内存使用完时 , 就直接把存活的对象复制到另外一块上 , 然后把已使用的那块空间一次清理掉 , 但是这种算法的代价就是内存的使用量缩小了一半 。 现代虚拟机都采用复制算法回收新生代 , 不过是把内存划分为了一个 Eden 区和两个 Survivor 区 , 比例是 8:1:1 , 每次使用 Eden 和其中一块 Survivor 区 , 也就是只有 10% 的内存会浪费掉 。 如果 Survivor 空间不够用 , 需要依赖其他内存比如老年代进行分配担保 。 复制算法在对象存活率比较高时效率是比较低下的 , 所以老年代一般不使用复制算法 。 标记整理算法即是在标记清除之后 , 把所有存活的对象都向一端移动 , 然后清理掉边界以外的内存区域 。
最后就是讲垃圾回收算法的具体应用了 , 也就是垃圾收集器 。
G1 及 ZGCGarbage First(G1)收集器是垃圾收集器技术发展历史上的里程碑式的成果 , 它开创了收集器面向局部收集的设计思路和基于 Region 的内存布局形式 。 它和 CMS 同样是一款主要面向服务端应用的垃圾收集器 , 不过在 JDK9 之后 , CMS 就被标记为废弃了 , G1 作为默认的垃圾收集器 , 在 JDK 14 已经正式移除 CMS 了 。 在 G1 收集器出现之前的所有其他收集器 , 包括 CMS 在内 , 垃圾收集的目标范围要么是整个新生代(Minor GC) , 要么就是整个老年代(Major GC) , 在要么就是整个 Java 堆(Full GC) 。 而 G1 是基于 Region 堆内存布局 , 虽然 G1 也仍是遵循分代收集理论设计的 , 但其堆内存的布局与其他收集器有非常明显的差异:G1 不再坚持固定大小以及固定数量的分代区域划分 , 而是把连续的 Java 堆划分为多个大小相等的独立区域(Region) , 每一个 Region 都可以根据需要 , 扮演新生代的 Eden 空间、Survivor 空间或者老年代 。 收集器根据 Region 的不同角色采用不同的策略去处理 。 G1 会根据用户设定允许的收集停顿时间去优先处理回收价值收益最大的那些 Region 区 , 也就是垃圾最多的 Region 区 , 这就是 Garbage First 名字的由来 。
G1 收集器的运作过程大致可划分为以下四个步骤:
1.初始标记
仅仅只是标记一下 GC Roots 能直接关联到的对象 , 这个阶段需要停顿线程 , 但耗时很短 。
2.并发标记
从 GC Root 开始对堆中对象进行可达性分析 , 递归扫描整个堆里的对象图 , 找出要回收的对象 , 这阶段耗时较长 , 但是可与用户程序并发执行 。
3.最终标记
对用户线程做另一个短暂的暂停 , 用于处理在并发标记阶段新产生的对象引用链变化 。
4.筛选回收
负责更新 Region 的统计数据 , 对各个 Region 的回收价值和成本进行排序 , 根据用户所期望的停顿时间来制定回收计划 。
G1 的目标是在可控的停顿时间内完成垃圾回收 , 所以进行了分区设计 , 但是 G1 也存在一些问题 , 比如停顿时间过长 , 通常 G1 的停顿时间在几十到几百毫秒之间 , 虽然这个数字其实已经非常小了 , 但是在用户体验有较高要求的情况下还是不能满足实际需求 , 而且 G1 支持的内存空间有限 , 不适用于超大内存的系统 , 特别是在内存容量高于 100GB 的系统上 , 会因内存过大而导致停顿时间增长 。
ZGC 在 JDK11 被引入 , 作为新一代的垃圾回收器 , 在设计之初就定义了三大目标:支持 TB 级内存 , 停顿时间控制在 10ms 之内 , 对程序吞吐量影响小于 15% 。
类加载机制虚拟机把描述类的数据从 Class 文件加载到内存 , 并对数据进行校验、解析和初始化 , 最终形成可以被虚拟机直接使用的 Java 对象 , 这就是虚拟机的类加载机制 。