垃圾收集器与内存分配策略
2018-07-02 14:19:37 0 举报
AI智能生成
垃圾收集器与内存分配策略 知识来源:《深入理解JAVA虚拟机:JVM高级特性与最佳实践》第三章
作者其他创作
大纲/内容
确定对象的存活情况
引用计数法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器+1,引用失效,计数器-1
微软COM、ActionScript 3、Python、Squirrel中有使用
JAVA虚拟机里没有使用
无法解决对象之间相互循环引用的问题
可达性分析算法
主流的商用程序语言的主流实现中,都使用可达性分析(Reachability Analysis)判断对象的存活状态
通过一系列的GC Roots对象向下搜索,走过的路径成为引用链,若A对象从所有GC Roots都不可达,则A对象不可用(可回收)
GC Roots
虚拟机栈(栈帧中的本地变量表)中引用的对象
即已压栈的方法中的对象引用
方法去中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中的JNI(本地/Native方法)引用的对象
回收方法区(永久代)
JDK1.8中已经完全移除了永久代(PermGen),使用新的方法区实现元空间(Metaspace)代替,元空间处于本地内存中
JDK1.7开始,字符串常量池已经转移到堆中
JAVA虚拟机规范中不要求虚拟机在方法去实现垃圾收集
常规应用进行一次垃圾手机一般可以回收70%~95%的空间,而永久代的垃圾手机效率远低于此
在大量使用反射、动态代理、CGLib等ByteCode(字节码)框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载功能,以保证永久代不会溢出
回收对象
废弃常量
常量池中的“abc”已经没有任何String对象引用,也没有其他地方引用这个字面量,此时发生GC,若必要,“abc”常量将被系统清理出常量池
常量池中的其他类(接口)、方法、字段的符号引用也与此类似
无用的类
该类的所有实例已经被回收
加载该类的ClassLoader已经被回收
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
垃圾收集算法
标记-清除(Mark-Sweep)
实现
标记所需要回收的对象,完成后统一回收
不足
标记和清除两个过程效率都不高
空间问题,清除后会产生大量内存碎片,导致分配大对象时找不到连续的内存空间而提前出发下一次GC
复制(Copying)
实现
将内存划分为大小相等的两块,一块用完时,把存活的对象复制到另外一块上面,然后清空自己
不足
内存缩小为原来的一半
扩展
商业虚拟机都采用这种方式回收新生代
IBM研究表明,新生代的对象98%是“朝生夕死”的,所以不需要按照1:1的比例划分内存,而是将内存氛围一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,Eden和Survivor默认8:1,即只损失10%的内存空间
HotSpot最初就是这种布局,与IBM研究并没有什么实际联系,此处借用来说明这种划分的价值所在
标记-整理(Mark-Compact)
实现
标记后,所有存活对象向一端移动,然后清理掉边界以外的内存
分代收集(Generational Collection)
根据对象存活周期不同将内存划分为鸡块,不同的区域采用不同的算法
垃圾收集器
Serial收集器(新生代)
最基本、发展历史最悠久的收集器
虚拟机运行在Client模式下的默认新生代收集器
桌面应用的场景中,分配给虚拟机管理的内存一般不会很大,新生代一般只有几十兆或一两百兆,Serial的GC停顿一般只有几十毫秒,所以对于Client模式,Serial依旧是一个很好的选择
Serial Old收集器(老年代)
Serial收集器的老年代版本,使用“标记-整理”算法
主要意义在于给Client模式下的虚拟机使用以及作为CMS收集器的后背预案
Parallel Scavenge收集器(新生代)
目标是达到一个可控制的吞吐量(Throughput),别名:吞吐量优先收集器
Parallel Old收集器(老年代)
Parallel Scavenge的老年代版本,使用多线程和“标记-整理”算法
JDK1.6开始提供
在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器
ParNew收集器(新生代)
Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都完全一样
CMS收集器的默认新生代收集器
CMS(Concurrent Mark Sweep)收录及其(老年代)
目标是获取最短回收停顿时间
CMS收集器对CPU资源非常敏感,默认启动回收线程数是(CPU数量+3)/4,即4个以上CPU时,使用不少于25%的CPU资源进行回收
JDK1.5下默认老年代使用了68%的空间后激活CMS收集器,JDK1.6中提升至92%
缺点
采用标记-清除算法,需要处理空间碎片
无法处理浮动垃圾(Floating Garbage)-即标记新产生的垃圾,可能出现Concurrent Mode Failure失败而导致另一次Full GC的产生
G1收集器
当今收集器技术发展的最前沿成果之一
是一款面向服务端应用的垃圾收集器,使命是(在比较长期的)未来可以替换掉JDK1.5中发布的CMS收集器
特点
并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或CPU核心)来缩短Stop-The-World停顿的时间
分代收集:与其他收集器一样,分代概念在G1中依然保留
空间整合:与CMS的“标记-清除”算法不同,G1从整体上看是基于“标记-整理”算法,从局部(两个Region之间)上来看是基于“复制”算法实现的,不会产生空间碎片
可预测的停顿:G1可以建立可预测的停顿时间模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾手机上的时间部的超过N毫秒
根据回收价值排序,每次根据允许的收集时间,优先回收价值最大的Region,也是G1收集器(Garbage-First)名称的由来
G1收集器将整个Java堆分成多个大小相等的独立区域(Region),最然还保留新生代和老年代的概念,但是他们不再是物理隔离的,二十一部分Region(不需要连续)的集合
参数
-XX:+SurvivorRatio 新生代中Eden和Survivor的容量比值,默认为8,即8:1
-XX+PretenureSizeThreshold 直接晋升到老年代的对象大小
-XX:+MaxTenuringThreshold 晋升到老年代的对象年龄,每个对象在坚持过一次Minor GC后,年龄+1,超过该值就会进入老年代
-XX:+UseAdaptiveSizePolicy 动态调整Java堆中各个区域的大小以及进入老年代的年龄
-XX:HandlePromotionFailure=true 是否允许分配担保失败,即老年代剩余空间不足以应付新生代的整个Eden和Survivorqud所有对象都存活的极端情况
-XX:+ParallelGCThreads 设置并行GC时进行内存回收的线程数,默认等于CPU数量
-XX:+UseSerialGC 使用Serial+Serial Old的收集器组合
-XX:+UserParallelGC 使用Parallel Scavenge+Serial Old收集器组合
-XX:+GCTimeRatio GC时间占总时间的比率,默认值为99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效
-XX:+MaxGCPauseMillis 设置GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效
-XX:+UseParallelOldGC 使用Parallel Scavenge+Parallel Old收集器组合
-XX:+UseParNewGC 使用ParNew+Serial Old收集器组合
-XX:+UseConcMarkSweepGC 使用ParNew+CMS+Serial Old收集器组合
-XX:+CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后触发垃圾收集,JDK1.5默认68%,JDK1.6默认92%,仅在使用CMS收集器生效
-XX:+UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否要进行内存碎片整理,仅在使用CMS收集器时生效
-XX:+CMSFullGCsBeforeCompation 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理,仅在使用CMS收集器时生效
-XX:+UseG1GC 使用G1收集器
知识来源:《深入理解JAVA虚拟机:JVM高级特性与最佳实践(第二版)》第三章
0 条评论
下一页