垃圾回收与内存分配

版权声明:转载请注明出处 https://blog.csdn.net/ty13572053785/article/details/85041378

一、判断对象是否存活

1.引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,引用计数器就加1;当引用失效时,引用计数器就减一;任何时刻引用计数器为0的对象就是不可能在被使用的。
缺点:很难解决循环引用的问题。
2.可达性分析算法(HotSpot使用此方法)
通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路程称为“引用链”。当一个对象到“GC Roots”没有任何引用链相连,说明此对象是不可用的。
可作为GC Roots的对象:
虚拟机栈(帧栈中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象。

二、Java中的引用

通过可达性分析算法判断对象的引用链是否可达,判断对象是否存活都与“引用”有关。引用可以分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Weak Reference)
1.强引用:在程序代码中普遍存在。类似“Object obj=new Object()”这类的引用,只要强引用还在,垃圾回收器永远不会回收掉被引用的对象。
2.软引用:描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统发生内存溢出前,将会把这些对象列进回收范围进行第二次回收。如果回收后还没有足够的内存,会跑出内存溢出异常。
3.弱引用:也是用来描述非必须对象的,它的强度比软引用更弱一些,只能存活到下一次垃圾回收发生前。
4.虚引用:最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会影响其生存时间,也无法通过虚引用来获得一个对象实例。为一个对象设置虚引用关联的唯一目的是使该对象被垃圾收集器回收时收到一个系统通知。

三、垃圾收集算法

1.标记–清除算法
       算法分为“标记”和“清除”两个阶段。首先标记出所有需要回收的对象,在标记结束后统一清除全部标记的对象。
       缺点:清除和标记这两个过程的效率都不高;标记清除后会产生大量的不连续的内存碎片。(空间碎片太多会导致以后在程序运行过程中需要分配较大对象时因无法找到足够大的连续内存而不得不提前进行一次垃圾回收)
2.复制算法
       将可用内存划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将其中还存活着的对象复制到另一块上面,然后再把已使用过的内存一次性清除掉。(常用于回收“新生代”)
3.标记–整理算法
       标记出所有需要回收的对象。然后让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。(常用于回收“老生代”)
4.分代收集算法
       根据对象存活周期的不同将对象分为几块。一般是把Java堆分为“新生代”和“老生代”,根据各个年代的特点采用最合适的收集算法。

四、HotSpot的算法实现

       GC进行时为了保持一致性必须停止所有Java执行线程。Stop The World!
       一致性:在整个执行期间整个系统看起来就像被冻结在某个时间结点上,不可以出现分析过程中对象引用关系还在不断变化的情况,该点不满足的话分析准确性就无法得到保证。
程序执行时只有在安全点上才能停下来GC。

五、垃圾收集器

在这里插入图片描述
1.Serial收集器
       新生代收集器,采用复制算法,单线程的收集器,它进行垃圾收集时,必须停止其他所有线程的活动,直到其收集结束。是VM运行在Client模式下的默认新生代收集器。
2.ParNew收集器
       新生代收集器,采用复制算法,Serial收集器的多线程版本,其余部分都与Serial收集器完全一样。
3.Parallel Scavenge收集器
       新生代收集器,采用复制算法,并行的多线程收集器。可以控制吞吐量。通常被称为“吞吐量优先收集器”。
吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集时间)
4.Serial Old收集器
       老年代收集器,采用标记–整理算法,单线程收集器。给Client模式下的虚拟机使用
5.Parallel Old收集器
       老年代收集器,使用多线程和标记–整理算法。是Parallel Scavenge的老年代版本。
6.CMS收集器(Concurrent Mark Sweep)
       是一种使回收停顿时间最短的收集器。主要用在互联网站或者B/S系统的服务端上,这类应用尤其注意相应速度,希望停顿时间最短,给用户带来较好的体验。
收集步骤:
1>初始标记(CMS initial mark):仅仅标记一下GC Roots能够关联到的对象。Stop The World,占用时间少
2>并发标记(CMS concurrent mark):进行GC Roots Tracing的过程。用时多,可与用户程序并发进行
3>重新标记(CMS remark):为了修正并发标记期间因用户程序运行而导致标记变动的那一部分对象的标记记录Stop The World,占用时间少
4>并发清理(CMS concurrent sweep):用时多,可与用户程序并发运行
整个过程中耗时时间最长的并发标记和并发清理都能和用户程序一起执行,所以从总体上来说CMS收集器的内存回收过程和用户线程是一起并发执行的。
CMS收集器的缺点:
1>CMS收集器对CPU资源非常敏感,收集器会占用一部分处理器(CMS默认开启的回收线程数是(CPU数量+3)/4)导致应用程序变慢,吞吐量降低。
2>CMS收集器无法处理浮动垃圾(收集过程中产生的垃圾)
3>CMS基于“标记–清除”算法实现,收集结束时会有大量的空间碎片产生。
7.G1收集器
并行与并发:G1收集器收集时能通过并发的方式让Java程序继续执行。
分代收集:新生代和老年代。
空间整合:采用标记–整理算法,收集结束后不会产生空间碎片
可预测的停顿:与CMS相比,不仅能够多了预测停顿时间的功能(通过建立可预测的停顿时间模型来实现)
收集步骤:初始标记、并发标记、最终标记、筛选回收。

六、内存分配策略

1.对象优先在Eden分配
2.大对象(例如很长的字符串及数组)直接进入老年代
3.长期存活的对象(经过多次GC不死)将进入老年代

七、Minor GC与Full GC

新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特征,所以Minor GC非常频繁,一般回收速度也比较快。
老年代GC(Full GC/Major GC):指发生在老年代的GC,出现了Full GC,经常会伴随至少一次的Minor GC。Full GC的速度一般比Minor GC的速度慢十倍以上。

猜你喜欢

转载自blog.csdn.net/ty13572053785/article/details/85041378