Java GC垃圾回收机制
Java提供了gc机制,jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.垃圾回收机制,java不需要向C或者C++那样需要程序员自行释放内存,由编程人员释放资源,避免了资源释放错误和不必要的失误而带来的程序上的错误,java提供的垃圾回收机制会在程序执行的过程中自动的判断哪些资源不再被利用变成无用的资源,自动调用垃圾回收机制回收无用的资源。
内存分配上java用的HotSpot虚拟机,会把堆区(存放对象)分为两个区域:年轻代,年老代,两个物理区域的对象可以互相转移
- 一个伊甸园空间(Eden ),非常自在的一块区域
- 两个幸存者空间(Survivor )
- 最开始创建的对象会被存放在Eden伊甸园里
- 当Eden中的对象经历了一次gc之后存活下来的对象会被移动到一个幸存者空间里(survivor),之后
- 每次gc之后存活来的对象都会存放在同一个存活区,直到这个空间满了,如果还有存活的就会存放在另一个存活空间,之后清空饱和的那个幸存者空间,
- 经历过几次以上的步骤几次之后还在存活的对象,就会被移动到年老代里面
垃圾收集算法:
它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
“复制”(Copying)算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。
”标记-压缩/整理“算法:复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
分代收集算法
新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;
老年代中,其存活率较高、没有额外空间对它进行分配担保,就应该使用“标记-整理”或“标记-清理”算法进行回收。
JDK7一共有5种GC类型:
- Serial GC
- Parallel GC
- Parallel Old GC (Parallel Compacting GC)
- Concurrent Mark & Sweep GC (or “CMS”)
- Garbage First (G1) GC
参考:
周志明. 深入理解Java虚拟机[M]. 北京:机械工业出版社, 2013: 61-100.