一、运行时数据区域
1、堆(线程共享)
存数组+对象,1.7后增加字符串常量池
2、虚拟机栈(线程私有)
局部变量表、操作数栈、动态链接、方法返回地址
3、本地方法栈(线程私有)
执行本地方法的区域
4、程序计数器(线程私有)
记录线程执行的字节码地址
5、方法区(线程共享)只是规范,下面是各版本的具体实现
JDK1.6 永久代:和堆使用的物理空间是连续的(可指定大小,超出报内存溢出),运行时常量池+类常量池+字符串常量池
JDK1.7 永久代:和堆使用的物理空间是连续的(可指定大小,超出报内存溢出),运行时常量池+类常量池,字符串常量池放在堆里
JDK1.8 元空间:本地内存中(机器内存多大,元空间多大),运行时常量池+类常量池,字符串常量池放在堆里
元空间替代永久代:PermSize和MaxPermSize决定了永久代的上限,很难设置的合适;加载多少类的元数据不再由MaxPermSize控制,而由系统的实际空用空间控制。
二、判断垃圾回收
1、引用计数法
2、可达性分析算法
GC ROOT:
虚拟机栈引用的对象、本地方法栈引用的对象、方法区的常量、静态变量引用的对象
三、垃圾回收算法
1、标记-清除法:内存碎片
2、复制法:可用内存减半,效率低(新生代)
3、标记-整理法:效率低(老年代)
4、分代收集策略:
年轻代(Eden:SurvivorFrom:SurvivorTo=8:1:1)
年轻代:老年代=1:2
Eden+SFrom->STo->老年代
5、对象进入老年代
年龄 ≥ 15;
大对象直接进入老年代;
在 S0(或S1) 区相同年龄的对象大小之和大于 S0(或S1)空间一半以上时,则年龄大于等于
该年龄的对象也会晋升到老年代。
6、
在发生 MinorGC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果大于,那么Minor GC 可以确保是安全的,如果不大于,那么虚拟机会查看HandlePromotionFailure 设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则进行 Minor GC,否则可能进行一次 Full GC。
四、新生代垃圾回收器
1、Serial:单线程(适合client模式的虚拟机)
2、ParNew:Serial的多线程版本(适合server模式的虚拟机),可与CMS配合
3、Parallel Scavenge:多线程,达到可控制的吞吐量,三个参数(最大垃圾收集时间、吞吐量大小和自适应策略开关)
五、老年代垃圾回收器
1、Serial Old:Serial的老年代版本,与Serial配合
2、Parallel Old:Parallel Scavenge的老年代版本,与Parallel Scavenge配合,实现吞吐量优先
3、CMS(Concurrent Mark Sweep)
优点:
最短STW时间,响应速度最快,大部分时间用户线程可运行
标记-清除算法:初始标记(STW)->并发标记->重新标记(STW)->并发清除
缺点:
CPU敏感,回收线程数=(CPU+1)/4
无法清理“并发清除”阶段用户线程产生的浮动垃圾,且需预留用户线程执行的空间
产生大量内存碎片,可通过设置开启内存碎片整理(默认开启),也可设置执行多少次不压缩的
Full GC 后跟着带来一次带压缩的。
六、G1(Garbage first)
各代的存储地址不连续,每一代都使用了 n 个不连续的大小相同的 Region。
(E:Eden,S:survivor,O:old,H:humongous )
初始标记->并发标记->最终标记->筛选回收
优点:
不会产生内存碎片(整体:标记-整理法,局部:复制算法)
可指定的停顿时间
传统的收集器如果发生 Full GC 是对整个堆进行全区域的垃圾收集,而分配成各个 Region 的话,方便 G1 跟踪各个 Region 里垃圾堆积的价值大小(回收所获得的空间大小及回收所需经验值),这样根据价值大小维护一个优先列表,根据允许的收集时间,优先收集回收价值最大的 Region,也就避免了整个老年代的回收,也就减少了 STW 造成的停顿时间。同时由于只收集部分 Region,可就做到了 STW 时间的可控。
七、总结
client模式:Serial+Serial Old
响应时间快:ParNew+CMS
吞吐量:Parallel Scavenge + Parallel Old
G1:需根据具体场景,调整参数