Java虚拟机探究之--垃圾回收机制
Java有自己的一套垃圾回收机制,为开发者省去了一大笔精力,这也是java相较于C++的一个很大的不同,探究一下垃圾回收机制是如何工作的和GC算法的原理。 一般来说清除垃圾要做一下两步,一.先检测垃圾 二.清除垃圾 先来说说如何检测垃圾,检测垃圾的常用算法有以下几种: 一.可达性分析算法 以根集(GC Root)对象为起始点进行搜索,
Java有自己的一套垃圾回收机制,为开发者省去了一大笔精力,这也是java相较于C++的一个很大的不同,探究一下垃圾回收机制是如何工作的和GC算法的原理。
一般来说清除垃圾要做一下两步,一.先检测垃圾 二.清除垃圾
先来说说如何检测垃圾,检测垃圾的常用算法有以下几种:
一.可达性分析算法
以根集(GC Root)对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象,这个GC Root对象一般来说可以是:虚拟机栈中引用的对象或者方法区常量池中引用的对象。如下图:
如Object678,虽然互相之间有引用,但是GC Root无法到达,即被检测会垃圾。
二.引用计数法
即给每个对象添加一个计数器,当有其他对象引用它时就+1,当引用失效的时候就-1,然后当这个对象的计数器为0的时候就判定它没有被任何对象引用,就把他当做垃圾回收。但是这个方法有一个bug,如果有两个对象A和B,互相引用,除此之外,没有其他任何对象引用它们,实际上这两个对象已经无法访问,即是我们说的垃圾对象。但是互相引用,计数不为0,导致无法回收.
上面介绍了两种检测垃圾的方法,接下来就说说垃圾回收的几种算法:
1.标记-清除(Mark-sweep)
标记所有需要回收的对象,然后统一回收。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。即他就是找到垃圾标记垃圾,然后清除垃圾,不做任何其他事,所以他 也就存在明显的缺点,那就是清除后内存里会长生大量的碎片,同时这种方法效率也比较低。可以看以下两张图理解这种算法:
2.复制(Copying)
即先开两个相同大的区域,一开始只使用其中的一个区域,当垃圾回收时,把当前区域中不是垃圾的对象都复制到另一个区域中,并且整理排列。所以这个算法很好的解决了上面产生大量“碎片”的缺点,而他同样也存在很明显的不足,那就是双倍的空间,这对内存来说显然是很可怕的。
3.标记-整理(Mark-Compact)
上面两个算法都有各自的优缺点,所以这个算法就是结合上面两个的优点。第一阶段标记所有被引用对象,第二阶段清除所有违未被引用的对象,并且把存活对象整理到堆的一块。所以此算法解决了碎片问题,也解决了空间浪费的问题。
接下来介绍的是目前JVM中使用的GC算法,即分代收集算法(这里当一代转向下一代【淘汰】时就会用上上面3种垃圾回收方法了)
说到分代收集算法,即内存堆(heap)中是分代的,下面贴一张java堆中各带分布图来说明下:
可以看到堆中是分为Young(年轻的),Old(年老的),Permanent(永久的)三代。
Young:主要是用来存放新生的对象。
Old:主要存放应用程序中生命周期长的内存对象。
Permanent:是指内存的永久保存区域,主要存放Class和Meta的信息
其中Young中又分为两阶段:Eden和Survivor。
Survivor又细分为From 和To。
讲一下两个概念Minor GC和Full GC的区别:
GC(或Minor GC):收集 生命周期短的区域(Young area),一开始比如Eden区里满了就会Minor GC来回收垃圾。
Full GC (或Major GC):收集生命周期比较长的区域(Old area),比如当年老代里满了就会触发Full GC。
GC 效率比较高,我们要尽量减少 Full GC 的次数。
young年轻代:所有对象刚产生的时候就是在这个地方。年轻代被分为3个部分——Enden区和两个Survivor区(From和to)当Eden区被对象填满时,就会执行Minor GC,执行完后Eden区被清空,并把所有存活下来的对象转移到其中一个survivor区的from区。Minor GC同样会检查存活下来的对象,并把它们转移到另一个survivor区(to区)。这样在一段时间内,总会有一个空的survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。需要注意,Survivor的两个区是对称的,没先后关系,from和to是相对的。
所以年轻代里GC垃圾回收其实就是不断的往前挪(Edent->From->To),不断的回收垃圾,然后多次GC后还存在的对象就进入年老代。这种方法其实就涉及了上面“复制”的垃圾回收方法。
Old年老代:在年轻代中经历了N次回收后仍然没有被清除的对象,就会被放到年老代中,都是生命周期较长的对象。对于年老代和永久代,采用的回收方法就不是像年轻代那样的“复制”回收算法。当老年代内存被占满时将会触发Full GC,回收整个堆内存。所以年老代的回收内存的方法其实是上面的“标记-整理”的方法。
Permanent持久代:用于存放静态文件,比如java类、方法等。持久代对垃圾回收没有显著的影响。
上面基本就是java虚拟机垃圾回收的这么一个过程。
更多推荐
所有评论(0)