java的GC垃圾回收整理

一、我们为什么要了解GC?

在真实项目中会时不时的发生OOM,尤其是大数据场景,真实应用中OOM更是不可避免的问题,如果不能合理的利用内存,将会直接影响我们项目的并发性;同样的GC也是由一个或者一组进程来实现的,它也占用我们JVM当中的deap、cpu。当GC成为我们程序并发度、性能运行的瓶颈的时候,我们就需要自己来管理这些“自动化”的技术了。

二、垃圾回收主要在哪里进行?

堆是垃圾回收的主要场所,其次是方法区

三、Minor GC,Major GC、Full GC
Minor GC:指发生在新生代的垃圾收集动作,非常频繁,速度较快。触发条件:当eden满的时候。
Major GC:指发生在老年代的GC,出现Major GC,经常会伴随一次Minor GC,同时Minor GC也会引起Major
GC,一般在GC日志中统称为GC,不频繁。触发条件:老年代满的时候。
Full GC:指发生在老年代和新生代的GC,速度很慢,需要Stop The World。触发条件:当准备发生minor GC的时候如果统计发现之前平均晋升的空间大于当前old gen的时候;minor GC后存货的对象>old gen剩余空间;永久代空间不足,会触发Full GC;system.gc();当年轻代,年老带都放不下的一个内存时;

四、GC实现机制

垃圾回收的场所有两个,一个是堆,一个是方法区。在堆中存储了程序运行时的所有对象信息,垃圾回收就是回收那些所谓的“已死亡”的对象,将其内存释放掉。java8虚拟机采用分块也就是分代回收方式,主要有新生代空间(Young)和老年代空间(Old),其中Young空间,按照8:1:1比例,又被分为2个部分和3个板块,分别是1个Egen区,和2个Survivor区,Eden区域是用来存放使用new或者newInstance等方式创建的对象,默认都是存放在Eden区,除非这个对象太大,或者超过了设定的阈值-XX:PretenureSizeThresold,这样的对象会被直接分配到Old区域,2个Survivor(幸存)区,一般称S0,S1,理论上他们是一样大的,说下他们的工作方式:在不断创建对象的过程中,Eden区会满,这时候会开始做Young G也叫Minor GC,而Young空间的第一次GC就是找出Eden区中,幸存活着的对象,然后将这些对象,放到S0,或S1区中的其中一个, 假设第一次选择了S0,它会逐步将活着的对象拷贝到S0区域,但是如果S0区域满了,剩下活着的对象只能放old区域了,接下来要做的是,将Eden区域 清空,此时时候S1区域也是空的。当第二次Eden区域满的时候,就将Eden区域中活着的对象+S0区域中活着的对象,迁移到S1中,如果S1放不下,就会将剩下的部分,放到Old区域中,只是这次对象来源区域增加了S0,最后会将Eden区+S0区域清空,第三次和第四次依次类推,始终保证S0和S1有一个是空的,用来存储临时对象,用于交换空间的目的,反反复复多次没有被淘汰的对象,将会放入old区域中,默认是15次。具体的交换过程就和上图中的信息相似。

关于永久代

在JDK1.8之前hotspot实现中,类的元数据如方法数据、方法信息(字节码、栈和变量大小)、运行时常量池、已确定的符号应用和虚拟方法表等被保存在永久代/持久代并且有默认大小32位64M、64位85M,也可以通过参数-XX:MaxPermSize进行设置,一旦类元数据超过大小便会OOM。而在JDK1.8之中,把永久代从java堆中移除了,类的元数据直接保存在了本地内存区域也就是堆外内存,称之为元空间。那么这么做有什么好处呢?有经验的同学会发现,持久代调优很困难,因为永久代的大小很难确定影响其因素也比较多,例如上面说的类的总数、常量池大小、方法数量,并且永久代的数据也可能会随着FUll GC而发生变化。JDK1.8当中类的元数据保存在了本地内存中,元空间的最大可分配空间就是系统可用内存空间,可以避免永久代的溢出。并且1.8之前常量池存在永久代,1.8转移到了堆当中。

 

五、如何判断对象是否存活

1、引用计数法

在对象上添加一个引用计数器,每当有一个对象引用它时,计数器加1,当使用完该对象时,计数器减1,计数器值为0的对象表示不可能再被使用。引用计数法实现简单,判定高效,但不能解决对象之间相互引用的问题。

2、可达性算法

通过一系列成为“GC roots”的对象作为起点,从这些节点开始向下搜索走过的路径称为“引用链”,当一个对象到GC roots没有任何引用链时,就判定为可回收对象。java采用此种算法判断是否需要进行回收。Java中什么样的可以作为GC roots的对象呢?

a、虚拟机栈(栈帧中的本地变量,本地变量表中的所有对象)中引用的对象

b、方法区中静态属性引用的对象。

c、方法区中常量引用的对象(java8之后运行时常量池转移到了堆中)

d、本地方法栈中引用的对象。

 

六、GC实现机制如何判断对象死亡,进行回收

通过知识点五,我们知道java是通过可达性分析算法来判断对象是否需要进行回收的,那么只要是不可达就会立即回收么?其实不然,真正要判断一个对象死亡,其实是需要进行两次标记的。首先是第一次对对象进行GC roots的可达性判断后进行第一次标记并进行一次筛选判断,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没必要执行”,对象被回收;第二次标记,如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer()方法中缓慢执行。这里所谓的执行是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢或者发生死循环,将导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。Finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果这中间对象重新与引用链上的任何的一个对象建立关联,比如把自己赋值给某个类变量或对象的成员变量,那么这个对象就会被移出这个回收集合。如果第二次时还没逃脱就真的被回收了。

七、垃圾回收中的收集算法

标记/清除算法,遍历所有GCroots,清除时对没有标识的进行回收,执行时stop the world,否则会干扰标记。这样的遍历所有效率不高,并且此种清理会产生不连续的空间碎片,浪费空间容易导致经常GC。

复制算法:将内存按照大小分为两块,每次使用一块,当这块使用完,将存活的复制到另一半上,同时也是按序排列,碎片少,然后将被复制的一次性清除。这种方式提高了效率,碎片问题也解决了但是浪费一半空间,这种算法就是新生代采用的算法,新生代中,通过研究发现不用1:1分配,一般也就是默认8:1:1方式分配即可,分为了eden与s0/1三个区域,同时为了避免意外,加入了担保机制,当S1不能存储eden+S0的内存时采用担保机制直接进入老年代。但是也可以想象,如果对象存活率高的话比如老年代,这种算法的优势明显降低。那么老年代又是什么算法呢?

标记/整理算法:这种算法标记方法与标记/清理算法一样都是遍历GC roots在header头上打标记,但是它在标记完不是直接清理而是先让所有活动对象向一端进行移动,然后一次性清理掉边界以外的内存。这也是老年代使用的算法。这样无需多块空间,不会产生碎片。

分代收集算法:根据对象存活周期的不同将内存分为几块。一般把Java堆分为新生代和老年代,根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾收集时有大批对象死去,只有少量存活,可以选用复制算法。而老年代对象存活率高,使用标记清除或者标记整理算法。

八、GC过程中的内存担保机制

      当出现大量对象在Minor GC后仍然存活的情况,就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。与生活中的银行贷款类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会存活下来在实际完后才能内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多空间。如果出现担保失败,就只好重新发起一次Full GC来进行内存的分配。

 

 

参考博文:

https://blog.csdn.net/u012998254/article/details/81428621

https://blog.csdn.net/qq_36314960/article/details/79923581

https://blog.csdn.net/luzhensmart/article/details/81431212

https://blog.csdn.net/xyh269/article/details/53106790

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值