HotSpot JVM 内存模型
- 内存概念上如下划分
- HotSpot内存参数
- -Xms; -Xmx 堆大小
- -Xss 栈大小
- -Xmn --XX:SurviorRatio=xxx 新生代大小 ;新生代中eden和survivor的比例
- -XX:NewRatio=xx;-XX:MaxTenuringThreshold 新生代和永久代的比例,新生代最大存活次数
- -XX:PermSize=xxx; -XX:MaxPermSize=xxx 永久代大小
- HotSpot JVM 参数可以分为标准参数(standard options)和非标准参数(non-standard options);
- 非标准参数则有因升级JDK而改变的可能
JVM 堆的申请和管理
- JVM 通过mmap系统调用向kernel申请,然后切分成不同的generation,之后vm通过virtual space进行管理,实现Java Heap的expand和shrink。
- 一次性申请大小:Xmx + MaxPermSize + 其他
Java对象模型(linux64bit)
- 规则
- 举例:一个HashMap对象图模型
HotSpot JVM GC策略可以按照下面几个维度总结
- GC工作单线程、多线程进行
- GC算法的实现中使用单线程完成内存回收工作,GC算法不需要解决同步等问题,缺点在于效率低下,没有利用多CPU资源
- GC算法通过多线程实现,充分利用多CPU资源,效率高
- JVM GC 是否和应用并发执行
- stop the world: JVM GC时停止应用---影响应用的latency响应时间
- 并发:JVM GC 和 应用一起在执行---会存在回收不干净,需要二次回收情况,即影响吞吐量
- 吞吐量 or latency的取舍:
- 吞吐量:垃圾回收时间与非垃圾回收时间的比值 > 通过-XX:MaxGCPauseMillis=<N>指定。为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。不用显示指定使用什么垃圾收集器
- 最大垃圾回收暂停:指定垃圾回收时的最长暂停时间 > 通过-XX:GCTimeRatio=<N>来设定,公式为1/(1+N)
- 如何处理内存回收后的碎片问题
- 保证GC后无碎片 压缩(标记-整理)GC算法 && 拷贝GC算法来实现
- 有碎片 不压缩(标记-删除)GC算法
HotSpot JVM GC 算法
1. DefNew
2. MSC
3. PS
4. PS MarkSweep
5. ParNew
6. CMS
- Copy
- 单线程,拷贝算法,不会产生内存碎片
- 用于young(eden+from),Minor GC
- 触发条件:Eden区满
- 算法流程: 根据对象的应用关系图,从root对象开始,只是move©活的对象到survivor to。(判断Young区对象是否活着也要参考old区的对象,利用card table)然后回收eden和survivor from
- MSC(Mark-Sweep-Compact)
- 单线程,标记-整理算法,不会产生内存碎片
- 用于young(eden+from)+old+perm,Full GC
- 触发条件:
- old区满;perm区满
- promotion failed
- concurrent mode failure
- YGC时的悲观策略:统计得到Minor GC引起的promotion需要old区空间大于当前old区剩余空间
- 显示调用System.gc() ,包括RMI等的定时触发
- dump live的内存
- 算法流程4阶段,单线程串行执行,每个阶段依次扫一遍内存
1. 单线程执行,从root开始深度优先搜索标记所有活的对象(等于做了CMS中的initial-mark和concurrent-mark,remark的活儿,但是是单线程且stop-the-world)
2. 计算所有标记的活对象新的内存地址(压缩后的位置)
3. 修改对象的内部引用,因为活对象要被压缩移到新内存位置了
4. 压缩移动对象(memcopy),根据第二阶段计算的位置
PS Scavenge
用于young(eden+from)区,Minor GC
多线程版本的Copy GC算法,减少了停机时间
PS MarkSweep
用于old、perm区, Full GC
此算法将整个old划分为多个region,并且基于一个假设:
经过上次GC compact后,越左边的区域,活对象密度要远远大于右边的区域。
所以就从左往右分析,当某个区域的密度达到一个值的时候,就认为这是一个临界区域,所以这个临界区域左边的区域,将不会进行压缩,而右边的区域,则会进行压缩。
算法流程
0. old或perm区被划分成为固定大小的多个region
1. Marking phase:多线程标记活对象
2. Summary phase:单线程执行,从左到右依次检查每个region的活对象密度,直到找到某个region的密度到了一个值得进行compact的值。此region的左侧区域叫做dense prefix,右侧region需要compact,并且计算了每个需要compact的region的压缩点信息。
3. Compact phase:多线程执行regioncompact
ParNew
concurrent collector(concurrentlow pause collector) 即Concurrent Mark-Sweep (CMS) Collector中的回收新生代的GC算法
用于young(eden+from)区,Minor GC
ParallelScavenge和ParNew都是并行GC,主要是并行收集younggen,目的和性能其实都差不多,区别如下
0. PS以前是广度优先顺序来遍历对象图的,JDK6的时候改为默认用深度优先顺序遍历,并留有一个UseDepthFirstScavengeOrder参数来选择是用深度还是广度优先。在JDK6u18之后这个参数被去掉,PS变为只用深度优先遍历。ParNew则是一直都只用广度优先顺序来遍历
1. S完整实现了adaptive sizepolicy,而ParNew及“分代式GC框架”内的其它GC都没有实现完(倒不是不能实现,就是麻烦+没人力资源去做)。所以千万千万别在用ParNew+CMS的组合下用UseAdaptiveSizePolicy,请只在使用UseParallelGC或UseParallelOldGC的时候用它。
2. 由于在“分代式GC框架”内,ParNew可以跟CMS搭配使用,而ParallelScavenge不能。当时ParNew GC被从Exact VM移植到HotSpot VM的最大原因就是为了跟CMS搭配使用。Reference
CMS
标记-清除算法,会产生内存碎片
用于old和Perm
触发条件: 1. 保证先于Serial MSC之前触发
0. –XX:CMSInitiatingOccupancyFraction=n 默认值68,当old区超过这个上限 则触发
1. CMSClassUnloadingEnabled 考虑perm区大小决定触发
2. 根据JVM的统计信息决定
3. ....
算法流程
0. inital mark: 1stpause,stop-the-world 仅仅标记root对象,在bitmap里面标记,单线程
1. concurrent marking: 不停机 多线程从初始活对象开始递归遍历标记所有活对象
2. preclean: 不停机 由于第二阶段不停机,由第一阶段标记的对象的引用状态可能变化了,remark阶段要再次停机标记,这里为了让remark阶段停机时间短一些而工作
3. abort-clean: 不停机
4. remark: 2ndpause,stop-the-world 由于concurrent marking阶段是不停机的,此时理论上存在新的活对象,此时要重新标记一次,但是应该工作量会小很多。多线程执行,确保所有活对象都被标记
5. concurrent sweeping: 不停机 单线程的删除无用对象;old区首地址开始一个个对象遍历
6. reset:CMS GC工作的收尾工作,比如回收bitmap等
不会进行压缩:
0. 不能使用bump-the-pointer technique,需要一个free list,记录所有被回收的内存。造成在old区分配内存时代价比较大
1. 在concurrent marking阶段是不停机的,所有有可能漏标记新的活对象,所以需要remark的工作;同时也有可能在initial mark阶段标记的活的对象无用了,结果就是误标记了死对象,造成回收不完全
2. 会产生碎片
HotSpot JVM 垃圾回收器
- GC 程度、范围
- YGC:新生代的GC;针对young(eden+from)
- FGC:全堆范围的GC;针对young(eden+from)+old+perm
- HotSpot JVM提供垃圾回收器以及特点
- serial collector 串行垃圾收集器
jvm client模式默认;参数 -XX:+UseSerialGC 显式设置
- DefNew,MSC
- parallel collector( throughput collector ) 并行垃圾收集器
使用-XX:ParallelGCThreads=<N>设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等
- J2SE 5.0 update 6 引入
-server模式默认;参数 -XX:+UseParallelGC 显式设置
- PS Scavenge;MSC
- J2SE 6.0 改进 Parallel Compacting Collector
设置参数-XX:+UseParallelOldGC,使老生代垃圾回收策略为并行压缩
- PS Scavenge; PS MarkSweep;MSC
- concurrent collector(concurrent low pause collector) 即Concurrent Mark-Sweep (CMS) Collector
-XX:+UseConcMarkSweepGC 使用CMS收集器,ParNew用于新生代,CMS用于旧生代、永久代
-XX:+UseCMSCompactAtFullCollection 默认CMS算法不进行压缩,这里打开压缩
-XX:+CMSClassUnloadingEnabled
diamond线上对于CMS的配置如上 还可用的配置
–XX:CMSInitiatingOccupancyFraction=n 默认值68,当old区超过这个上限 则触发
> -XX: CMSMaxAbortablePrecleanTime=5(单位为ms)
- ParNew, CMS, MSC