贵阳网站建设公司哪个好,禹城网站设计,上交所互动平台,做物流行业网站的开发公司文章目录 #x1f334;死亡对象的判断算法#x1f338;引用计数算法#x1f338;可达性分析算法 #x1f333;垃圾回收算法#x1f338;标记-清除算法#x1f338;复制算法#x1f338;标记-整理算法#x1f338;分代算法#x1f338;哪些对象会进入新生代#xff1f… 文章目录 死亡对象的判断算法引用计数算法可达性分析算法 垃圾回收算法标记-清除算法复制算法标记-整理算法分代算法哪些对象会进入新生代哪些对象会进入老年代 经典面试题⭕总结 Java运行时内存的各个区域。对于程序计数器、虚拟机栈、本地方法栈这三部分区域而言其生命周期与相关线程有关随线程而生随线程而灭。
并且这三个区域的内存分配与回收具有确定性因为当方法结束或者线程结束时内存就自然跟着线程回收了。因此我们本节课所讲的有关内存分配和回收关注的为Java堆与方法区这两个区域。
Java堆中存放着几乎所有的对象实例垃圾回收器在对堆进行垃圾回收前首先要判断这些对象哪些还存活哪些已经死去。判断对象是否已死有如下几种算法
在 Java 中所有的对象都是要存在内存中的也可以说内存中存储的是一个个对象因此我们将内存回收也可以叫做死亡对象的回收
死亡对象的判断算法
引用计数算法
引用计数描述的算法为: 给对象增加一个引用计数器每当有一个地方引用它时计数器就1当引用失效时计数器就-1任何时刻计数器为0的对象就是不能再被使用的即对象已死。
引用计数法实现简单判定效率也比较高在大部分情况下都是一个不错的算法。比如Python语言就采用引用计数法进行内存管理。
但是在主流的JVM中没有选用引用计数法来管理内存最主要的原因就是引用计数法无法解决对象的循环引用问题
对象的循环引用是指当两个或多个对象互相持有对方的引用通常是通过智能指针导致它们的引用计数永远不会降为零从而导致内存泄漏的情况。
可达性分析算法
在上面我们讲了Java并不采用引用计数法来判断对象是否已死而采用可达性分析来判断对象是否存活(同样采用此法的还有C#、Lisp-最早的一门采用动态内存分配的语言)。
此算法的核心思想为 : 通过一系列称为GC Roots的对象作为起始点从这些节点开始向下搜索搜索走过的路径称之为引用链当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时证明此对象是不可用的。以下图为例 对象Object5-Object7之间虽然彼此还有关联但是它们到GC Roots是不可达的因此他们会被判定为可回收对象。
在Java语言中可作为GC Roots的对象包含下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中 JNI(Native方法)引用的对象。
从上面我们可以看出“引用”的功能除了最早我们使用它引用来查找对象现在我们还可以使用“引用”来判断死亡对象了。
所以在 JDK1.2 时Java 对引用的概念做了扩充将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种这四种引用的强度依次递减。 强引用 : 强引用指的是在程序代码之中普遍存在的类似于Object obj new Object()这类的引用只要强引用还存在垃圾回收器永远不会回收掉被引用的对象实例。软引用 : 软引用是用来描述一些还有用但是不是必须的对象。对于软引用关联着的对象在系统将要发生内存溢出之前会把这些对象列入回收范围之中进行第二次回收。如果这次回收还是没有足够的内存才会抛出内存溢出异常。在JDK1.2之后提供了SoftReference类来实现软引用。弱引用 : 弱引用也是用来描述非必需对象的。但是它的强度要弱于软引用。被弱引用关联的对象只能生存到下一次垃圾回收发生之前。当垃圾回收器开始进行工作时无论当前内容是否够用都会回收掉只被弱引用关联的对象。在JDK1.2之后提供了WeakReference类来实现 弱引用。虚引用 : 虚引用也被称为幽灵引用或者幻影引用它是最弱的一种引用关系。一个对象是否有虚引用的存在完全不会对其生存时间构成影响也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后提供了PhantomReference类来实现虚引用。 垃圾回收算法
通过上面的判断算法我们可以将死亡对象标记出来。标记出来之后我们就可以进行垃圾回收操作了接下来我们来看下垃圾回收机器使用的几种算法
标记-清除算法
标记-清除算法是最基础的收集算法。
算法分为标记和清除两个阶段 : 首先标记出所有需要回收的对象在标记完成后统一回收所有被标记的对象(标记过程见3.1.2章节)。
后续的收集算法都是基于这种思路并对其不足加以改进而已。
标记-清除算法的不足主要有两个 :
效率问题 : 标记和清除这两个过程的效率都不高空间问题 : 标记清除后会产生大量不连续的内存碎片空间碎片太多可能会导致以后在程序运行中
需要分配较大对象时无法找到足够连续内存而不得不提前触发另一次垃圾回收
复制算法
复制算法是为了解决标记-清理的效率问题。它将可用内存按容量划分为大小相等的两块每次只使用其中的一块。
当这块内存需要进行垃圾回收时会将此区域还存活着的对象复制到另一块上面然后再把已经使用过的内存区域一次清理掉。这样做的好处是每次都是对整个半区进行内存回收内存分配时也就不需要考虑内存碎片等复杂情况只需要移动堆顶指针按顺序分配即可。
此算法实现简单运行高效。算法的执行流程如下图 :
现在的商用虚拟机(包括HotSpot都是采用这种收集算法来回收新生代)
新生代中98%的对象都是朝生夕死的所以并不需要按照1 : 1的比例来划分内存空间而是将内存(新生代内存)分为一块较大的Eden(伊甸园)空间和两块较小的Survivor(幸存者)空间每次使用Eden和其中一块Survivor两个Survivor区域一个称为From区另一个称为To区域。
当回收时将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上最后清理掉Eden和刚才用过的Survivor空间。
当Survivor空间不够用时需要依赖其他内存(老年代)进行分配担保。
HotSpot默认Eden与Survivor的大小比例是8 : 1也就是说Eden:Survivor From : Survivor To 8:1:1。
所以每次新生代可用内存空间为整个新生代容量的90%,而剩下的10%用来存放回收后存活的对象
HotSpot实现的复制算法流程如下: 当Eden区满的时候,会触发第一次Minor gc,把还活着的对象拷贝到Survivor From区当 Eden区再次触发Minor gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。当后续Eden又发生Minor gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到 From区域,并将Eden和To区域清空。部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数 MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代 标记-整理算法
复制收集算法在对象存活率较高时会进行比较多的复制操作效率会变低。因此在老年代一般不能使用复制算法。
针对老年代的特点提出了一种称之为标记-整理算法。标记过程仍与标记-清除过程一致但后续步骤不是直接对可回收对象进行清理而是让所有存活对象都向一端移动然后直接清理掉端边界以外的内存。类似于链表删除中间元素 流程图如下:
分代算法
分代算法和上面讲的 3 种算法不同分代算法是通过区域划分实现不同区域和不同的垃圾回收策略从而实现更好的垃圾回收。这就好比中国的一国两制方针一样对于不同的情况和地域设置更符合当地的规则从而实现更好的管理这就是分代算法的设计思想。
当前 JVM 垃圾收集都采用的是分代收集(Generational Collection)算法这个算法并没有新思想只是根据对象存活周期的不同将内存划分为几块。
一般是把Java堆分为新生代和老年代。在新生代中每次垃圾回收都有大批对象死去只有少量存活因此我们采用复制算法而老年代中对象存活率高、没有额外空间对它进行分配担保就必须采用标记-清理或者标记-整理算法
哪些对象会进入新生代哪些对象会进入老年代
新生代一般创建的对象都会进入新生代
老年代大对象和经历了 N 次一般情况默认是 15 次垃圾回收依然存活下来的对象会从新生代移动到老年代
经典面试题
请问了解Minor GC和Full GC么这两种GC有什么不一样吗 Minor GC又称为新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性因此Minor GC(采用复制算法)非常频繁一般回收速度也比较快。Full GC 又称为 老年代GC或者Major GC : 指发生在老年代的垃圾收集。出现了Major GC经常会伴随至少一次的Minor GC(并非绝对在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上 ⭕总结
感谢大家的阅读希望得到大家的批评指正和大家一起进步与君共勉