【java】垃圾回收机制与算法

之前看了一些java的垃圾回收机制的文章,感觉全网都有,但是写的参差不齐,有的写的超好,有的总觉得怪怪的,所以还是自己整理一番!(如有雷同,纯属抄袭。。。)

哎,“寒冬”啊,大家日子都不好过!

概念:

垃圾回收机制就是当你写你的java代码的时候,基本不需要考虑内存资源的占用和释放,为什么说基本不需要考虑呢,因为java有垃圾回收机制来帮你做这件事情,但是为什么又没有说完全不考虑呢?因为如果你的代码写的稀烂,OOM是很常见的操作!所以垃圾回收机制,就是帮你清理掉已经没有用的内存占用(比如一些用完不会再用的对象),但是我们需要减少GC

 

垃圾回收算法:

那市面上都有哪些常见的垃圾回收算法呢?

1、标记-清除法(Mark-Sweep)

很好理解,两个步骤,先找到存活的对象(标记),接着当执行垃圾回收的时候,清除其他对象。是最原始最基础最简单的算法

缺点:对象被删除之后,没有整理过堆(heap),结果产生大量不连续的内存碎片,当我们创建一个大对象(大对象需要连续的一整块内存,比如一个很长的集合,Map等),就会导致提前触发下一次的GC

这张图来自:https://www.cnblogs.com/jiangtunan/p/11025521.html(作者:将图南)

 

2、复制算法(Copying)

怕内存被碎片化,所以提出新的解决方案。

直接把内存砍成两半(叫A和B),在使用A的时候,B是空的,当触发垃圾回收的时候,把A中存活的对象复制到B中,然后清空A,接着新来的对象,就放入B中,以此类推循环往复。

缺点:太明显了好吗,我买了16G的内存,结果最大只能用8G????还有另外一个缺陷,对象存活率比较高,那么就会出现一大部分数据反复的复制过来复制过去

这张图来自:https://blog.csdn.net/yrwan95/article/details/82829186(作者:蜜汁蛋总)

 

3、标记-整理算法(Mark-Compact)

有一些垃圾回收器,使用了标记-清除法(Mark-Sweep)之外,还会周期性的调用标记-整理算法,用来对堆的碎片进行定期的整理!

先找到存活的对象(标记)

然后将存活的对象按照一定的规则挪到同一侧(或者同一边),

更新存活对象的应用的指针值(因为实际堆中的对象换了位置,所以栈中对象的引用指针需要更新)

然后清空边界以外的所有对象

这其中还涉及到按照什么规则排?

(具体我就不展开了,大家有兴趣可以看如下pdf,应该是需要翻墙的)

http://www.cs.tau.ac.il/~maon/teaching/2014-2015/seminar/seminar1415a-lec2-mark-sweep-mark-compact.pdf

缺点:这种算法其实还蛮不错的了

缺点就是需要更多的空间来存储引用(不过java本身new对象的时候就是引用+堆中的对象)

通常比标记-清除复制算法更慢,需要更多的堆的传递(因为把堆上的对象做了一定规则的排序)

pdf中的原文:

Has some space overheads incurred bystoring forwarding addresses.

 Usually has a slower throughput than marksweep or copying GC, as it requires more passes over the heap.

这张图来自:https://blog.csdn.net/wuzhiwei549/article/details/80563134(作者:1Vincent)
 

4、分代收集算法(Generational Collection)

分代要满足一个假设:绝大多数的对象生命周期都非常短暂,甚至有些用一次之后就不用了!

核心:把内存分为两部分,两部分使用不同的垃圾收集机制

比如分为新生代和老年代,新生代每次都有大比例的对象死去,那就可以用复制算法,老年代,对象存活率高,就可以用标记整理,java就是这么做的!

 

java中垃圾回收简述:

java垃圾回收就是分代收集算法的一种延伸,每个内存空间又有自己独有的处理方式

1、对象先创建到Eden中

2、当新对象创建的时候,Eden区已经放不下了,就会发生一次Minor GC,这是小规模的GC手段,目的就是清理Eden空间,将Eden中存活的对象挪到Survivor的From中,等到下一次Minor GC时候,会将Eden中存活的+SurvivorFrom中存活的全部挪到To中,下一次再反过来挪到From中,存活的对象每挪动一次,年龄就会+1。以此类推

啥时候年轻代的对象会进入老年代呢?

内存分配与回收策略(对象晋升老年代):https://www.jianshu.com/p/0772dc04e1d9(作者:是一动不动的friend)

3、当有对象进入到老年代,如果老年代满了就会清理老年代,这是一个非常耗时的GC(我们需要尽量避免触发这类的GC)

为什么我这里没有写Full GC呢,因为具体我也不确定到底触发的是哪种,我估计是Full GC,大家如果想深入研究可以看如下的链接

  • Major GC 是清理老年代。
  • Full GC 是清理整个堆空间—包括年轻代和老年代。

Minor GC、Major GC和Full GC之间的区别:https://www.cnblogs.com/tuhooo/p/7508503.html(作者也是转载的)

如下图是java的heap中内存分配的比例 :

 

java官网相关参数的设置和默认值:https://www.oracle.com/java/technologies/vmoptions-jsp.html

JVM内存配置详解:https://www.cnblogs.com/qmfsun/p/5396710.html(作者:Agoly)

 

我们是可以通过不同的参数来设置这些值的比例的,具体还是要根据自己项目来判定

每个个接口每秒会接受多少请求,每个请求会创建多大的对象,每秒会有多少对象进入到Eden,这些对象是否是临时对象等等一系列问题
 

最后的最后,说一个问题,看到很多文章说垃圾回收算法是找到要需要删除的对象,然后删除那些对象,我想这样说是错误的,应该是找到存活的对象,那么通过什么手段寻找呢?

叫做可达性分析算法(GC Roots,大概来说就是从一些可以作为“根”(root)的节点往下寻找存活的对象,并且标记这些(存活)对象(具体也不是我三言两语就能说明白的)。

大家有兴趣可以去研究下

JAVA垃圾回收-可达性分析算法:https://blog.csdn.net/luzhensmart/article/details/81431212(翻译作者:这瓜保熟么)

 

菜鸡一只,正在努力提升自己,本文如果有哪些写的不对的,欢迎大家指出,我一定及时更改!

(接下来想看看java不同垃圾回收的区别,比如什么G1,什么CMS),下次再见拜拜~

发布了77 篇原创文章 · 获赞 91 · 访问量 25万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章