深入理解Java虚拟机之垃圾收集器内存分配策略

时间:2022-12-27 12:34:23
垃圾回收器(Gabage Collection,GC)在jvm中非常重要的一个机制,设想,我们只创建分配内存空间,而不对其进行回收,内存再大也会超出我们的范围。
接下来我们了解一下三个问题:
1.哪些内存需要回收
2.什么时候回收
3.如何回收

1.哪些内存需要回收?

答:对象已死(即不可被任何途径使用的对象)。

1.1.怎么判断对象已死?

答:通过引用
( 1)引用计数算法。给对象添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器减一;任何时刻引用为0时,表示对象不能在用。
弊端:当相互循环引用时,将达不到其效果。java没有选用其作为内存管理。
(2)根搜索算法(GC Roots Tracing)。通过一系列的名为”GCRoots“的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象GC Roots没有任何引用链相连(用图论的话就是不可达)时,证明对象不可用。
Java中可作为GC Roots的对象包括:
虚拟机栈中的引用对象;
方法区的类静态属性引用的对象;
方法区常量引用对象;
本地方法栈中JNI(Native方法)的引用对象。

1.2.四种引用类型:

1.强引用:直接创建或实例化的对象,只要强引用还在,垃圾回收器永远不会回收掉被引用的对象。
2.软引用:用来描述一些还有用,但并非必须的对象。当系统发生内存泄露时,会先将这部分进行回收,如果回收后内存仍不满足,才报内存溢出异常。
3.弱引用:用来描述非必须对象,比软引用弱,被弱引用关联的对象只能生存到下一次垃圾回收之前。当垃圾收集器工作时,不论系统内存是否有空闲,都将其回收。
4.虚引用:最弱的一种引用,一个对象有虚引用,不会对其有任何影响,也不能通过虚引用取得对象实例。目的是为了对象呗回收时,收到系统通知。

1.3生存还是死亡:

在根搜索算法中,不可达的并不是非死不可,还有经过至少两次标记;当一个对象被根搜索认为不可达时,就将其标记,并进行一次筛选,条件为是否有必要执行finalize()方法。如果这个对象有必要,就放到一个低优先级线程中去执行,而并不关注其是否执行完,如果在finalize方法中,对象又获得了引用,则对象又重新变成活的,不被GC回收,进行下一次根搜索的筛选。
finalize()只会执行一次。强烈不推荐在此使用自我拯救。

2.垃圾收集算法

2.1.标记-清楚算法(Mark-Sweep)

将需要回收的对象内存进行标记,标记完成后,对其进行统一的回收。
弊端:效率不高;会产生大量碎片。

2.2.复制算法

将内存划分为大小相等的两块,每次只使用其中一块,当着一块用完时,将这一块存活的对象全部复制到另一块上,并将这一块的全部进行回收。如此重复使用。
优点:实现简单,效率高,不会产生碎片。
弊端:会将原来的内存缩小一半。

2.2.1.复制算法扩展:

现在商业虚拟机都用其回收新生代。因为新生代都是一些朝生夕死的,所以并不需要1:1的比例分配内存,而是将内存分为一块较大的Eden和两块较小的Survivor的空间,每次使用Eden和一块Survivor空间。当回收时,将Eden和Survivor中存活的对象全部复制到另一块Survivor上,并将Eden和刚使用的Survivor全部清理。HotSpot虚拟机上这三者的比例为Eden:Survivor:Survivor=8:1:1。
也就是新生代的内存为90%,只有10%被浪费了
当Survivor中的内存不够用时(因为我们无法保证对象的每次回收都回收的Survivor能装满),可以依赖其他内存(老年代)进行分配担保

2.3.标记-整理算法

对标记-清理的一种改进,先进行标记,再将存活的向一段移动,之后再全部清理。这样就不会产生碎片。但这更降低了效率。

2.4.分代收集算法

将上面的算法进行结合,分为新生代和老年代,对其使用不同的收集算法;
新生代:复制算法;
老年代:标记-整理或标记-清理算法。

3.垃圾收集器

不同代有不同的收集器,连线的表明能协同使用的收集器:

深入理解Java虚拟机之垃圾收集器内存分配策略

3.1.Serial收集器:

1.单线程收集器
2.新生代收集器
3.在垃圾回收的使用会暂停所有工作线程。
4.对运行与Client模式下的虚拟机是很好用的。

3.2.Parnew收集器:

1.多线程收集器。
2.和Serial收集器基本一致,区别尽在能够多线程进行收集。

3.3.Parallel Scavenger收集器:

1.在新生代中,与1,2收集器相比,它们的目的是不同的;
2.Parnew的目的是为了让1的停顿时间尽可能的短,Parallel Scavenger是为了增加吞吐量,即CPU的效率。
3.高吞吐量会更快的完成任务,低停顿时间,会让用户交互变得美妙。
4.主要用于进行后台的处理。
5.有自动调节机制。

3.4.Seral Old收集器:

1.seral的老年代版本
2.单线程收集器,采用标记-整理算法

3.5.Parallel Old收集器:

1.Parallel Scavenger的老年代版本
2.和Parallel Scavenger收集器联合使用,在多核心的cpu上极大的提高了吞吐率

3.6.CMS收集器:

1.目的:以获取最短回收停顿时间
2.利用标记-清除算法
3.四个步骤:初始标记——并发标记——重新标记——并发清除
4.初始标记和重新标记仍需要“停止一切工作进程”
5.并非标记和清除可以和用户进程一起进行
6.主要特点:并发收集低停顿
7.缺点:对CPU资源很敏感,因为需要和工作线程并发执行;无法处理浮动垃圾,因为在并发处理的过程中很有可能产生新的垃圾,而此次标记已经结束;会产生碎片;

3.7.G1收集器:

1.JDK1.7以后的版本
2. 可以像CMS收集器一样,GC操作与应用的线程一起并发执行
3.紧凑的空闲内存区间且没有很长的GC停顿时间.
4. 需要可预测的GC暂停耗时.
5.不想牺牲太多吞吐量性能.
6. 启动后不需要请求更大的Java堆.

3.8.垃圾回收器各参数:

深入理解Java虚拟机之垃圾收集器内存分配策略