JVM内存分配和回收策略
2017-02-28 17:43 louistz 阅读(144) 评论(0) 编辑 收藏 举报JVM内存
- 堆
- 方法区(又叫非堆,运行时常量池属于方法区的一部分)
- 虚拟机栈
- 本地方法栈
- 程序计数器
- 直接内存(NIO)
PermGen to Meta Space
JDK8 HotSpot JVM中,PermGen将被完全移除,设置PermSize和MaxPermSize将会忽略并告警。类元信息将被存储在本地内存,这部分空间被称为“元空间(meta space)”. Metaspace默认不限制,可通过MaxMetaspaceSize参数设置大小。持续的元空间垃圾回收,说明存在类、类加载器导致的内存泄漏或者是大小不合适
垃圾回收
对象已死?
- 可达性分析(引用计数?循环引用?)
- GCRoots
- 虚拟机栈引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- 引用标记次数
- 方法区中类的回收
- 该类的实例都被回收
- 加载该类的ClassLoader被回收
- 该类对应的java.lang.Class对象没有被引用,无法再任何地方通过反射访问该类的方法
回收算法
- 标记-清除 (碎片问题)
- 复制算法 (回收新生代,98%朝生夕死)
- 标记-整理 (存活率大,复制的代价大)
- 分代收集 (存活少用复制,存活多用标记整理)
分代回收
Minor GC
- s1和eden区有幸存对象,s0清空
- s1和eden的幸存对象复制到s0, 完成后清空s1和eden
- 如果对象过大(-XX:PretenureSizeThreshold)或者对象存活次数(-XX:MaxTenuringThreshold)过多,直接分配到old区
- 或者S区中所有年龄相同的对象的大小总和超过S区空间大小的一半,那么年龄大于或者等于该年龄的对象就直接进入old区,而不用等到MaxTenuringThreshold的值
Major GC
如果某次MajorGC 后,老年代无法为将要晋升到老年代的对象做担保,那么久需要执行一次MajorGC(FullGC). 一般一次MajorGC会伴生着一次MinorGC ,但这并不是必需的。(如ParallelScavenge收集器的收集策略就有直接进行MajorGC的策略选择过程)
垃圾回收器
- Serial 新生代,最古老最基本的,单线程。Client模式下的好选择
- ParNew 新生代,Serial的多线程版本。Server模式下的好选择,只有它能与CMS配合工作
- Parallel Scavenge 新生代,复制算法,并行多线程。目标是可控吞吐量(吞吐量=用户代码时间/(用户代码时间+GC时间))
- Serial Old 老年代,标记-整理算法,单线程。Client模式下使用。Server模式下:一是jdk1.5之前与Parallel Scavenge配合,二是CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
- Parallel Old 老年代,Parallel scavenge的老年代版本。吞吐量优先和CPU资源敏感的场合: Parallel Scavenge + Parallel Old.
- CMS 老年代。目标是获取最短回收停顿时间。标记-清除算法。
CMS回收器介绍
运作过程
- 初始标记 (stop the world)
- 并发标记
- 重新标记 (stop the world)
- 并发清除
缺点
- 对cpu资源敏感。 占用一部分线程,导致程序慢,总吞吐量降低。 默认 线程数= (cpu + 3)/4. 这样来看,在cpu超过4个时,占用不少于25%的cpu资源,并且随着cpu数的增加而下降。但当cpu少于4个,影响很大。 为了应付这种情况,提供了一种“增量式并发收集器 i-CMS”,但效果不好(gc和用户线程交替运行),不再提倡。
- 无法处理浮动垃圾。可能出现“Concurrent Mode Failure” 失败而导致另一次FullGC的产生。
- 标记-清除算法导致的空间碎片。 无法找到连续的大空间来分配对象,而出发FullGC. 参数 -XX:+UseCMSCompactAtFullCollection 参数默认开启。默认是0,只的是每次FullGC都进行碎片整理。
G1回收器
暂留
常用垃圾回收器参数
java堆设置常用参数