如何设置自己网站的关键词,app制作价格,wordpress判断使用不同模板,长沙网站建设论坛【后端面经-Java】JVM垃圾回收机制 1. Where#xff1a;回收哪里的东西#xff1f;——JVM内存分配2. Which#xff1a;内存对象中谁会被回收#xff1f;——GC分代思想2.1 年轻代/老年代/永久代2.2 内存细分 3. When#xff1a;什么时候回收垃圾#xff1f;——GC触发条… 【后端面经-Java】JVM垃圾回收机制 1. Where回收哪里的东西——JVM内存分配2. Which内存对象中谁会被回收——GC分代思想2.1 年轻代/老年代/永久代2.2 内存细分 3. When什么时候回收垃圾——GC触发条件4. Why凭什么说它是垃圾——垃圾判断算法4.1 引用计数法4.2 可达性分析法 5. How如何对待垃圾——垃圾回收算法5.0 垃圾的垂死挣扎5.1 标记-清除算法5.2 标记-整理算法5.3 复制算法5.4 分代收集算法 6. Who谁去处理垃圾——垃圾回收器6.1 年轻代-Serial收集器6.2 年轻代-ParNew收集器6.3 年轻代-Parallel Scavenge收集器6.4 老年代-SerialOld收集器6.5 老年代-ParallelOld收集器6.6 老年代-CMSConcurrent Mark Sweep收集器6.7 年轻代和老年代-G1Garbage First收集器6.8 垃圾回收器对比图 面试模拟参考资料 1. Where回收哪里的东西——JVM内存分配
JVM垃圾回收机制Garbage Collect简称GC主要负责回收JVM内存当中未被及时释放回收的内存区域JVM垃圾回收机制让程序员摆脱了手动释放内存的操作降低了程序员疏忽大意导致的风险。 那么垃圾回收机制到底针对哪一块的内存空间进行处理呢是整体内存还是某一块内存 在回答这个问题之前我们需要先了解一下JVM内存分配机制JVM内存分配机制主要有如下几个区域
栈Stack堆Heap方法区Method Area本地方法栈Native Method Stack程序计数器PC 我们需要知道的是栈、本地方法栈、程序计数器和方法调用有关是线程私有的随着方法结束栈和本地方法栈的栈帧会出栈释放内存因此这三部分并不需要使用垃圾回收机制。 而堆主要存放对象实例和数组而方法区存放类的信息、编译信息、常量池等这两块区域由于是线程共享的在方法调用结束之后也不会被释放内存毕竟A用完了不知道B、C、D会不会用到这部分需要使用垃圾回收机制进行内存回收。 关于每个区域的具体内容可参考博客【后端面经-Java】JVM内存分区详解 因此内存回收机制主要针对堆和方法区进行处理。
2. Which内存对象中谁会被回收——GC分代思想
2.1 年轻代/老年代/永久代
JVM垃圾回收机制中一般都是基于GC分代思想进行算法设计。 GC机制将内存内容分为三部分
年轻代Young Generation新产生的实例基本上都处于这代因为新产生的实例大部分都是一次性的因此这部分内存需要经常进行内存回收老年代Old Generation在新生代残酷频繁的筛选机制中多次存活下来的实例会进入老年代老年代意味着生命周期较长一般在内存没有满之前不会对这部分内存进行回收永久代Permanent GenerationJCK1.8之后改成元空间Metaspace永久代存放的是JVM程序运行相关的元数据比如类信息、方法信息、常量池等内容重要且占空间小因此基本上不会进行垃圾回收。
如果要类比的话年轻代就是刚刚步入职场的小青年不稳定性较高很容易被裁员垃圾回收而熬过这个阶段成为技术骨干老年代之后基本上不会在正常公司运行过程中被裁员除非公司倒闭内存已满而永久代或者元空间就是公司最高层的管理人员对公司的运行起着关键作用一般情况下不会被裁员。
2.2 内存细分
堆和方法区 堆中存放年轻代和老年代而方法区中存放永久代。新生代区域细分 新生代区域又分为Eden区、Survivor0区和Survivor1区比例为8:1:1。
整体内存细分情况如图所示
3. When什么时候回收垃圾——GC触发条件
GC按照触发条件可分为Scavenge GC和Full GC。
Scavenge GCMinor GC Scavenge GC是指年轻代Eden区的垃圾回收触发条件为 年轻代Eden区域内存不足 调用Scavenge GC之后将未清理的元素放入Survivor0区域。如果Survivor0区域内存不足则将Survivor0区域内存中的所有元素放入Survivor1区域。清空Survivor0区域然后将Survivor1中的元素放入Survivor0中如果Survivor1区域内存不足则将Survivor1区域内存中的所有元素放入老年代中如果老年代也内存不足则会考虑触发Full GC。 Full GCMajor GC Full GC是整体对内存的垃圾回收包括年轻代和老年代触发条件为 老年代内存不足永久代内存不足显性调用system.gc()上次GC之后堆内存的划分出现变化
对于GC线程其本身的优先级比较低因此在CPU空闲的时候可能会进行GC处理而在忙时基本上不会进行GC处理除非此时内存空间不足需要GC处理之后才能正常运行。 Full GC对于计算资源是一个很大的消耗应该尽量避免使用Full GC。
4. Why凭什么说它是垃圾——垃圾判断算法
前面主要介绍了JVM垃圾回收机制的针对对象GC分代思想和触发条件。那么好好的一个对象实例GC机制空口无凭凭什么说它是垃圾呢这就需要垃圾判断算法了。 常见的垃圾判断方法有两种引用计数法和可达性分析法。
4.1 引用计数法
每个对象实例都有一个引用计数器当有一个引用指向该对象实例时引用计数器加1当引用失效时引用计数器减1当引用计数器为0时该对象实例就是垃圾需要进行回收。补充一下JVM的引用类型如下图所示优点判断逻辑简单缺点无法解决循环引用的问题(从图论角度来说就是环状节点无法识别)。
4.2 可达性分析法
将每个实例看作节点两个实例之间的引用关系看作路径。从GC Roots开始对堆内存中的对象进行遍历如果某个对象实例没有被遍历到则说明该对象实例不可达不可达则是垃圾对其进行垃圾标记等待后续回收非连通图查找连通子图的数量。GC Roots指的是正在运行的程序中一些基本对象从这些对象往下查找其引用对象包括如下几种 虚拟机栈栈帧中的本地变量表中引用的对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中JNINative方法引用的对象 优点能有效解决循环引用的问题缺点判断逻辑复杂需要遍历整个内存空间效率较低。
5. How如何对待垃圾——垃圾回收算法
常见的垃圾回收算法包括标记-清除算法、标记-整理算法、复制算法和分代收集算法自适应算法。
5.0 垃圾的垂死挣扎
在被处决之前垃圾会进行一次垂死挣扎实例第一次被标记为垃圾之后如果可以进行一次有效finalize()方法调用和其他实例建立引用那么该实例就会被复活不会被回收。
5.1 标记-清除算法
在垃圾判断算法执行完成后已经被明确判断成垃圾的实例清除法在原地释放其内存空间将其标记为可用空间等待后续的内存分配。
优点操作简单原地清除不需要复制内存缺点会产生内存碎片长期运行会影响CPU运行效率
5.2 标记-整理算法
标记-整理算法在标记完成之后将所有存活的实例移动到一端然后清除掉另一端的内存空间这样就可以有效解决内存碎片的问题。
优点无内存碎片缺点需要移动实例效率较低
5.3 复制算法
复制算法将内存空间分为两块每次只使用其中一块当一块内存空间内存满了之后将存活的实例复制到另一块内存空间中然后清除掉之前的内存空间。
优点无内存碎片操作简单标记和复制可并行缺点可用内存空间直接减半内存利用率较低
5.4 分代收集算法
针对不同代的数据特点使用不同的垃圾回收算法。
年轻代复制算法 存活对象较少复制算法每次的对象复制不会有太大负担操作频繁复制算法标记和复制可并发处理效率较高 老年代标记-整理算法 存活对象较多减少内存碎片提高可用性 永久代元空间不作考虑
6. Who谁去处理垃圾——垃圾回收器
垃圾回收器是垃圾回收算法的执行者常见的垃圾回收器如下图所示
连线部分说明这两个垃圾回收器能够搭配使用。
6.1 年轻代-Serial收集器
回收算法复制算法单线程执行回收算法的时候是单线程适用于并发能力较低的系统。Stop the World一般线程和回收线程无法并行执行回收算法需要中断其他线程这个现象称为Stop the World乱入Dio的“咋瓦鲁多”。 Stop the World现象会导致系统暂停引出垃圾收集停顿时间这一参数影响用户体验因此需要尽量避免 优点简单高效适用于单核CPU缺点无法并行且单线程处理效率较低启用方式-XX:UseSerialGC
6.2 年轻代-ParNew收集器
回收算法复制算法多线程执行回收算法的时候是多线程也会存在Stop the World现象除了多线程的改进之外和Serial收集器没有太大区别优点多线程处理效率高适用于多核CPU缺点一般线程和回收线程无法并行处理启用方式-XX:UseParNewGC
6.3 年轻代-Parallel Scavenge收集器
回收算法复制算法关注吞吐量吞吐量 用户线程运行时间 / 用户线程运行时间 垃圾回收线程运行时间相关参数 垃圾收集停顿时间 设置方式-XX:MaxGCPauseMillis一个数值停顿时间过大将会直接影响每次垃圾回收的用户体验停顿时间过小则会导致垃圾回收频繁 吞吐量大小 设置方式-XX:GCTimeRatio一个数值默认取值为99%表示只有1%的时间用于垃圾回收 自适应模式 设置方式-XX:UseAdaptiveSizePolicy设置自适应模式之后内存中的新生代分配比例和老年代的时间参数可自行调整达到吞吐量、停顿时间和内存占用的平衡 是JDK1.8的默认垃圾回收器启用方式-XX:UseParallelGC
6.4 老年代-SerialOld收集器
回收算法标记-整理算法单线程可类比年轻代的Serial收集器优缺点同理CMS收集器的后备算法
6.5 老年代-ParallelOld收集器
回收算法标记-压缩算法关注吞吐量可类比年轻代的Parallel Scavenge收集器多线程线程数通过-XX:ParallelGCThreads设置默认为CPU核心数启用方式-XX:UseParallelOldGC
6.6 老年代-CMSConcurrent Mark Sweep收集器
回收算法标记-清除算法关注停顿时间期望有较短的垃圾回收停顿时间从而优化用户体验回收步骤 初始标记适用可达性分析法的思想标记GC Roots能直接关联到的对象速度较快并发标记一般线程和回收线程并行对此时标记状态出现变化的实例进行统计重新标记根据并发标记的结果对标记状态发生变化的实例进行重新标记这一步相对较慢并发清除清理垃圾实例释放内存空间这一步可以和一般线程并行 相关参数 触发阈值 设置方式-XX:CMSInitiatingOccupancyFraction一个数值和之前讨论的何时进行垃圾回收的触发机制不同CMS在处理老年代的时候不会等到内存完全占满而是会设置一个阈值默认数值为68%占用内存空间超过这个阈值就进行垃圾回收处理。 整理标记 设置方式-XX:UseCMSCompactAtFullCollection如果设置这一标记则垃圾回收之后会进行一次整理合并内存碎片。 优点并发收集提高执行效率减少停顿时间用户体验佳缺点无法处理浮动垃圾对CPU资源敏感且标记清除算法会产生内存碎片
6.7 年轻代和老年代-G1Garbage First收集器
回收算法整体来看是标记-整理算法局部来看是复制算法关注停顿时间也关注高吞吐量我全都要.jpg分区不同于之前所讨论的分代划分内存区域的方式G1回收器将内存划分为一个又一个单元区域称为Region 设置方式-XX:G1HeapRegionSize一个数值每个分区内部可以存放年轻代或者老年代的数据根据不同的存放数据将分区划分为四类 E-Eden区存放年轻代当中Eden区域的数据S-Survivor区存放年轻代当中Survivor区域(survivor0和survivor1)的数据O-Old存放老年代的一般数据H-Humongous存放老年代当中大对象的数据当占据整个Region一半以上的时候就会被划分为Humongous区域 停顿预测模型用户可以自行设置垃圾回收停顿时间而G1回收器会根据历史数据构建预测模型考虑为了满足用户设置的停顿时间本次垃圾回收可以处理哪几个Region。Region优先级队列G1浏览器维护一个Region队列高价值的Region有更高的优先级在垃圾回收的时候优先处理这也是Garbage First名字的由来。回收步骤和CMS回收器类似 初始标记并发标记重新标记并发清除 优点并发操作并行收集提高执行效率关注停顿时间可预测停顿时间优化用户体验可处理浮动垃圾不会产生内存碎片
6.8 垃圾回收器对比图
对上述垃圾回收器的对比如下所示
面试模拟 Q介绍一下JVM和垃圾回收机制。 A从where、“which”、“when”、“why”、“how”、who的角度重点介绍触发机制/判断算法/垃圾回收算法/垃圾回收机制。 参考资料
JVM之垃圾回收机制GCJVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)深入理解 JVM 垃圾回收机制及其实现原理