jvm的垃圾回收机制

一、基本概念

1. Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理
2. 由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存
3. Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用

二、判断对象死亡的两种常用算法

1.引用计数算法
1)概念:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的

2)优点:算法的实现简单,判定效率也高,大部分情况下是一个不错的算法。很多地方应用到它

3)缺点:

	(1)引用和去引用伴随加法和减法,影响性能

	(2)致命的缺陷:对于循环引用的对象无法进行回收
2. 根搜索算法
1)概念:设立若干种根对象,当任何一个根对象(GC Root)到某一个对象均不可达时,则认为这个对象是可以被回收的

2)可达性分析:从根(GC Roots)的对象作为起始点,开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的

3)在JAVA语言中,可以当做GC roots的对象有以下几种

	(1)栈(栈帧中的本地变量表)中引用的对象

	(2)方法区中的静态成员

	(3)方法区中的常量引用的对象(全局变量)

	(4)本地方法栈中JNI(一般说的Native方法)引用的对象

	注:第一和第四种都是指的方法的本地变量表,第二种表达的意思比较清晰,第三种主要指的是声明为final的常量值

4)在根搜索算法的基础上,现代虚拟机的实现当中,垃圾搜集的算法主要有三种,分别是标记-清除算法、复制算法、标记-整理算法。这三种算法都扩充了根搜索算法	

三、垃圾回收算法

1.标记-清除算法(Mark-Sweep)
1)概念:

	(1)标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象

	(2)清除阶段:清除所有未被标记的对象

2)缺点

	(1)标记和清除的过程效率不高(标记和清除都需要从头遍历到尾)

	(2)标记清除后会产生大量不连续的碎片	
	
2. 复制算法:(新生代的GC)(Copying)
1)概念:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象

2)优点:

	(1)这样使得每次都是对整个半区进行回收,内存分配时也就不用考虑内存碎片等情况

	(2)只要移动堆顶指针,按顺序分配内存即可,实现简单,运行效率高

3)缺点:空间的浪费	
3. 标记-整理算法:(老年代的GC)(Mark-Compact)
1)复制算法在对象存活率高的时候要进行较多的复制操作,效率将会降低,所以在老年代中一般不能直接选用这种算法

2)概念:

	(1)标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象

	(2)整理阶段:将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间

3)优点:不会产生内存碎片

4)缺点:在标记的基础之上还需要进行对象的移动,成本相对较高,效率也不高	
4. 前三种算法对比:
1)效率:复制算法 > 标记/整理算法 > 标记/清除算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)

2)内存整齐度:复制算法=标记/整理算法>标记/清除算法

3)内存利用率:标记/整理算法=标记/清除算法>复制算法

注:

	(1)标记-整理算法不仅可以弥补标记-清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价

	(2)以看到标记/清除算法是比较落后的算法了,但是后两种算法却是在此基础上建立的

	(3)时间与空间不可兼得
5. 分代收集算法(Generational Collection)
1)当前商业虚拟机的GC都是采用的“分代收集算法”,只是根据对象的存活周期的不同将内存划分为几块儿。一般是把Java堆分为新生代和老年代:短命对象归为新生代,长命对象归为老年代

2)存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC

3)存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC

4)老年代的对象中,有一小部分是因为在新生代回收时,老年代做担保,进来的对象;绝大部分对象是因为很多次GC都没有被回收掉而进入老年代

四、垃圾收集器

在这里插入图片描述

1.Serial收集器:(串行收集器)

在这里插入图片描述

1)这个收集器是一个单线程的收集器,但它的单线程的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程(Stop-The-World:将用户正常工作的线程全部暂停掉),直到它收集结束

2)当它进行GC工作的时候,虽然会造成Stop-The-World,但它存在有存在的原因:正是因为它的简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,没有线程交互的开销,专心做GC,自然可以获得最高的单线程手机效率。所以Serial收集器对于运行在client模式下是一个很好的选择(它依然是虚拟机运行在client模式下的默认新生代收集器)。
2. Serial Old收集器
1)老年代单线程收集器,Serial收集器的老年代版本
3. ParNew收集器:Serial收集器的多线程版本(使用多条线程进行GC)

在这里插入图片描述

1)它是运行在server模式下的首选新生代收集器,除了Serial收集器外,目前只有它能与CMS收集器配合工作

4. Parallel Scavenge收集器(并行收集器)
1)类似ParNew,但更加关注吞吐量。目标是:达到一个可控制吞吐量的收集器

2)停顿时间和吞吐量不可能同时调优。我们一方买希望停顿时间少,另外一方面希望吞吐量高,其实这是矛盾的。因为:在GC的时候,垃圾回收的工作总量是不变的,如果将停顿时间减少,那频率就会提高;既然频率提高了,说明就会频繁的进行GC,那吞吐量就会减少,性能就会降低

3)吞吐量:CPU用于用户代码的时间/CPU总消耗时间的比值,即=运行用户代码的时间/(运行用户代码时间+垃圾收集时间)。比如,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%	
5. Parallel Old收集器
1)	Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先 
6. G1收集器:是当今收集器发展的最前言成果之一
1)它最大的优点是结合了空间整合,不会产生大量的碎片,也降低了进行gc的频率	

2)可以让使用者明确指定指定停顿时间。(可以指定一个最小时间,超过这个时间,就不会进行回收了)

3)它有了这么高效率的原因之一就是:对垃圾回收进行了划分优先级的操作,这种有优先级的区域回收方式保证了它的高效率

4)如果你的应用追求停顿,那G1现在已经可以作为一个可尝试的选择;如果你的应用追求吞吐量,那G1并不会为你带来什么特别的好处
7. CMS收集器(以上所有的收集器当中,当执行GC时,都会stop the world,但是CMS收集器却不会这样,老年代收集器)
1)CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短
2)CMS收集器运行过程:(着重实现了标记的过程)

在这里插入图片描述


(1)初始标记(根可以直接关联到的对象,速度快)

(2)并发标记(和用户线程一起,主要标记过程,标记全部对象)

(3)重新标记(由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正)

(4)并发清除(和用户线程一起,基于标记结果,直接清理对象)
3)初始标记和重新标记时,需要stop the world。整个过程中耗时最长的是并发标记和并发清除,这两个过程都可以和用户线程一起工作
4)优点:并发收集,低停顿
5)缺点:
(1)导致用户的执行速度降低

(2)无法处理浮动垃圾。因为它采用的是标记-清除算法。有可能有些垃圾在标记之后,需要等到下一次GC才会被回收。如果CMS运行期间无法满足程序需要,那么就会临时启用Serial Old收集器来重新进行老年代的收集

(3)由于采用的是标记-清除算法,那么就会产生大量的碎片。往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次full GC
6)既然标记-清除算法会造成内存空间的碎片化,CMS收集器为什么使用标记清除算法而不是使用标记整理算法
(1)CMS收集器更加关注停顿,它在做GC的时候是和用户线程一起工作的(并发执行),如果使用标记整理算法的话,那么在清理的时候就会去移动可用对象的内存空间,那么应用程序的线程就很有可能找不到应用对象在哪里	

五、GC的执行机制

1.Minor GC(Scavenge GC)
(1)Minor GC是发生在新生代中的垃圾收集动作,采用的是复制算法

(2)对象在Eden和From区出生后,在经过一次Minor GC后,如果对象还存活,并且能够被to区所容纳,那么在使用复制算法时这些存活对象就会被复制到to区域,然后清理掉Eden区和from区,并将这些对象的年龄设置为1,以后对象在Survivor区每熬过一次Minor GC,就将对象的年龄+1,当对象的年龄达到某个值时(默认是15岁,可以通过参数 --XX:MaxTenuringThreshold设置),这些对象就会成为老年代。但这也是不一定的,对于一些较大的对象(即需要分配一块较大的连续内存空间)则是直接进入老年代

(3)因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来	

(4)MinorGC回收原则: 每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率
2.Full GC
1)Full GC是发生在老年代的垃圾收集动作,采用的是标记-清除/整理算法

2)老年代里的对象几乎都是在Survivor区熬过来的,不会那么容易死掉。因此Full GC发生的次数不会有Minor GC那么频繁,并且做一次Full GC要比做一次Minor GC的时间要长

3)另外,如果采用的是标记-清除算法的话会产生许多碎片,此后如果需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次GC	

4)如下原因可能导致Full GC:  

	(1)年老代(Tenured)被写满

  (2)持久代(Perm)被写满

  (3)System.gc()被显示调用

  (4)上一次GC之后Heap的各域分配策略动态变化

六、减少GC开销的措施

1. 不要显式调用System.gc()
1)此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数
2. 尽量减少临时对象的使用
1)临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
3. 对象不用时最好显式置为Null
1)一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率
4. 尽量使用StringBuffer,而不用String来累加字符串
1)由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对每次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象
5. 能用基本类型如Int,Long,就不用Integer,Long对象
1)基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量
6. 尽量少用静态对象变量
1)静态变量属于全局变量,不会被GC回收,它们会一直占用内存
7. 分散对象创建或删除的时间
1)集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会 		

参考网址

Java虚拟机详解----JVM常见问题总结

关于jvm的垃圾回收机制

注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值