摘要
总结了下jvm 的通用的gc算法以及概念。主要记录思路为
- 问题定义
- 回收哪些对象
- 如何回收
- 相关阅读
- 自带问题
GC具体如何回收,回收机制最后落实成什么样,跟jvm内部实现也有很大关系,比如jdk1.8 与1.7的回收机制就已经有不同了。具体实现需要对版本具体研究,此处之只记录下通用的一些概念。
问题定义
GC即垃圾回收,jvm提供自动回收内存垃圾机制。自动回收内存中无用的对象,释放其占用的资源,以给后续使用。
回收哪些对象
首先,从哪里回收,从jvm 内存模型来看,线程私有的内存区域(java方法栈、native方法栈、程序计数器)生命周期随线程生灭,不存在回收问题。所以jvm回收的内存区域目标为(堆,方法区)。
其次,哪些对象是无用的对象。对于jvm来说,没有被你的程序使用的对象即为无用对象。
如何回收
如何从内存中回收对象,这个问题可以分成两部走:找出无用对象,回收无用对象占用的空间以便后续使用
找出无用对象
这里通常会讨论两种算法:引用计数法,GC Root(拓扑算法)。
如何回收
回收无用对象也可分为两步:回收对象占用的空间,清理剩余空间。
一般常用的回收算法有:
1.标记-清除:将标记的无用对象占用空间清空,这样回收会比较快,缺点是回收完的可用内存是零散分布的,不方便后续使用。
2.复制算法:可用内存空间分成两份,每次只用其中一份,回收时,将当前存活的对象复制到另一份空间中,当前空间完全清空,这样能保证内存中空间一定是连续的,方便后续内存分配。缺点为使用中每次只能使用一般的空间,浪费内存,适用于少量内存存活的场景。
3.标记-整理:即在标记-清除的基础上,整理清理后打散的内存空间为连续分布。这个算法使用于大量内存存活的场景。
4.分代整理:其实就是之前三种算法的结合使用。
这里对JVM 使用分代整理算法 使用理解很有帮助的一点是:
研究发现,jvm的对象大多存活事件很短,也就是书上所说的“朝生夕死”。
试想,jvm 1min内创建了100m的对象,但是每次gc后只留了10m的存活对象,这种情况下选复制算法比较划算。
但是其实gc完成后只有1/10的对象存活,则用来做复制的备份内存并不需要1/2,也只需要1/10即可。
以上这就是jvm 常说的“新生代”,新生代分为(eden, survivor),其中所有新建的对象都在eden区,surviror即为复制算法的两个备份区(s0, s1),默认eden:s0:s1=8:1:1,基本这个配比思想为java 新创建的对象能存活下来的比例1/10 。只需要分1/10的空间来存储gc后存活的对象。
survivor的设计初衷为保留“朝令夕死”的少量短命对象,所以suvivor必然是一块很小的区域。当内存中存活的对象越来越多时候,根据“存活的越久的对象就越有可能继续存活”这种思想,把存活期超过一定阀值的对象从survivior区中搬出,移到"老年代"中,即老年代用来保存存活期较久的对象。老年代的设计初衷为保存大量可能继续存活的对象。这种区域标记-整理算法比较适合,所以老年代一般使用标记-整理。
新生代+老年代,基本就是jvm的堆内存划分了。
方法区回收,则主要回收类对象、常量。
相关阅读
1.http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
2.《深入理解Java虚拟机》
3.http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html
自带问题
在oom试验中,设置了perSize=10m maxPerSize=10m ,通过string.intern循环产生string常量,却一直没有perm gen oom.查看jmap -heap ,发现内存中ps old gen 已经600M+, per gen 2m+.为什么?
网友评论