Java GC垃圾回收机制

Java提供了gc机制,jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.垃圾回收机制,java不需要向C或者C++那样需要程序员自行释放内存,由编程人员释放资源,避免了资源释放错误和不必要的失误而带来的程序上的错误,java提供的垃圾回收机制会在程序执行的过程中自动的判断哪些资源不再被利用变成无用的资源,自动调用垃圾回收机制回收无用的资源。

内存分配上java用的HotSpot虚拟机,会把堆区(存放对象)分为两个区域:年轻代,年老代,两个物理区域的对象可以互相转移


年轻代(新生代):大部分对象都会存在这里,因为很多对象创建之后很快就会变得不可达,在这里进行的大都是比较小而且操作频繁的回收,花费的时间短
年老代(老年代):内存相对较大,那些在年轻代被存活下来的对象也会被放到这里,对象会保留时间较长的,都会分布在年老代,处在这里的对象大都是占用空间上升缓慢,并且回收频率较低的对象,花费的时间较长
其中还有一块叫做持久区的区域permanent generation,也叫方法区,存放的是类的信息,可能在这里发生gc但是主要还是在年轻代和年老代发生gc
在年老代里存在类似于身份证的区域card table,存放的是所有年老代d对象指向年轻代的对象的引用,当针对年轻代发生gc的时候,只需要来查询这里来决定是否回收,不用再去遍历整个年老代,这样虽然带来了内存的开销,但是节省了时间


详细介绍年轻代:年轻代又被分为三个区域,
  •  一个伊甸园空间(Eden ),非常自在的一块区域
  •  两个幸存者空间(Survivor
  1. 最开始创建的对象会被存放在Eden伊甸园里
  2. 当Eden中的对象经历了一次gc之后存活下来的对象会被移动到一个幸存者空间里(survivor),之后
  3. 每次gc之后存活来的对象都会存放在同一个存活区,直到这个空间满了,如果还有存活的就会存放在另一个存活空间,之后清空饱和的那个幸存者空间,
  4. 经历过几次以上的步骤几次之后还在存活的对象,就会被移动到年老代里面

注意:刚刚创建的对象会保存在Eden里面的,对于那些长期存在的对象会从幸存区转移到年老代里
对于年老代的gc几乎发生在年老代满的时候,执行的过程会根据回收的类型利用不同的方式,
垃圾收集算法:
标记-清除”(Mark-Sweep)算法,算法分为“标记”和“清除”两个阶段:最开始标记那些需要进行回收的对象,在所有要回收的对象标记之后,执行清楚操作。回收无用的内存。

它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制”(Copying)算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。

标记-压缩/整理“算法:复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

分代收集算法

新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;

老年代中,其存活率较高、没有额外空间对它进行分配担保,就应该使用“标记-整理”或“标记-清理”算法进行回收。


JDK7一共有5种GC类型:
  1. Serial GC
  2. Parallel GC
  3. Parallel Old GC (Parallel Compacting GC)
  4. Concurrent Mark & Sweep GC  (or “CMS”)
  5. Garbage First (G1) GC
Serial GC:该回收算法第一步会先标记依然存活的对象,第二步,从头开始检查堆内存,清理那些死亡的对象,保留存活的对象
Parallel GC:与第一种算法最大的区别就是采用了多线程,效率会更高

Parallel Old GC (Parallel Compacting GC):算法和Parallel GC只在清理的步骤不一样,会把对象放到一个预先处理好的区域
CMS GC:所用的时间最少,也是最先标记,在标记的时候,其他线程依然会运行,节省时间,带来的缺点,就是相比于其他的gc会占用更多的内存和cpu资源,因为在标记的时候其他线程也在运行,默认情况下,这种回收算法回收的内存空间不是压缩的,不是连续的,前两个都是连续的,会导致内存碎片,只是节省了时间

G1 GC:一种最快的回收算法,他没有年轻代和老年代的概念,每次创建的对象会放在一个区域的不同的格子上,直到满了会执行一次gc,但是目前不稳定。
一般情况下调优不会介绍
JVM 调优主要在3点:
1. 选择合适的 GC collector.
2. 指定合适的 heap area大小.
3. 指定young generation的比例(或大小),它影响到 Full GC发生的频率以及应用暂停时间.

参考:
周志明. 深入理解Java虚拟机[M]. 北京:机械工业出版社, 2013: 61-100.
posted @ 2017-11-10 21:29  In_new  阅读(430)  评论(0编辑  收藏  举报