一、垃圾回收算法
标记-清除(Mark-Sweep):
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。
复制(Copying):
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
标记-整理(Mark-Compact):
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
分代收集(Generational Collecting):基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
串行收集:串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。
并行收集:并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。
并发收集:相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。
二、垃圾回收器
2.1 串行回收器
新生代串行回收器
(1)特点:
–它仅仅使用单线程进行垃圾回收
–它是独占式的垃圾回收
–进行垃圾回收时, Java应用程序中的线程都需要暂停(Stop-The-World)
–使用复制算法
–适合CPU等硬件不是很好的场合
(2)设置参数:
-XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 当以client模式运行时, 它是默认的垃圾收集器
老年代串行回收器
(1)特点:
–同新生代串行回收器一样, 单线程, 独占式的垃圾回收器
–通常老年代垃圾回收比新生代回收要更长时间, 所以可能会使应用程序停顿较长时间
(2)设置参数:
-XX:+UseSerialGC 新生代, 老年代都使用串行回收器
-XX:+UseParNeGC 新生代使用ParNew回收器, 老年代使用串行回收器
-XX:+UseParallelGC 新生代使用ParallelGC回收器, 老年代使用串行回收器
2.2 并行回收器
新生代ParNew回收器
(1)特点:
–将串行回收多线程化,
–使用复制算法
–垃圾回收时, 应用程序仍会暂停, 只不过由于是多线程回收, 在多核CPU上,回收效率会高于串行回收器, 反之在单核CPU, 效率会不如串行回收器
(2)设置参数:
-XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器
-XX:+UseConcMarkSweepGC 新生代使用ParNew回收器, 老年代使用CMS回收器
-XX:ParallelGCThreads=n 指回ParNew回收器工作时的线程数量, cpu核数小时8时, 其值等于cpu数量, 高于8时,可以使用公式(3+((5*CPU_count)/8))
新生代ParallelGC回收器
(1)特点:
–同ParNew回收器一样, 不同的地方在于,它非常关注系统的吞吐量(通过参数控制)
–使用复制算法
–支持自适应的GC调节策略(3)设置参数:
-XX:+UseParallelGC 新生代用ParallelGC回收器, 老年代使用串行回收器
-XX:+UseParallelOldGC 新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系统吞吐量的控制:
-XX:MaxGCPauseMillis=n(单位ms) 设置垃圾回收的最大停顿时间,
-XX:GCTimeRatio=n(n在0-100之间) 设置吞吐量的大小, 假设值为n, 那系统将花费不超过1/(n+1)的时间用于垃圾回收
-XX:+UseAdaptiveSizePolicy 打开自适应GC策略, 在这种模式下, 新生代的大小, eden,survivior的比例, 晋升老年代的对象年龄等参数会被自动调整,以达到堆大小, 吞吐量, 停顿时间之间的平衡点
老年代ParallelOldGC回收器
(1)特点:
–同新生代的ParallelGC回收器一样, 是属于老年代的关注吞吐量的多线程并发回收器
–使用标记压缩算法,
(2)设置参数:
-XX:+UseParallelOldGC 新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器, 是非常关注系统吞吐量的回收器组合, 适合用于对吞吐量要求较高的系统
-XX:ParallelGCThreads=n 指回ParNew回收器工作时的线程数量, cpu核数小时8时, 其值等于cpu数量, 高于8时, 可以使用公式(3+((5*CPU_count)/8))
2.3 CMS回收器(Concurrent Mark Sweep,并发标记清除)
老年代的并发回收器
(1)特点:
–是并发回收, 非独占式的回收器, 大部分时候应用程序不会停止运行
–针对年老代的回收器,
–使用并发标记清除算法, 因此回收后会有内存碎片, 可以使参数设置进行内存碎片的压缩整理
–与ParallelGC和ParallelOldGC不同, CMS主要关注系统停顿时间
(2)CMS主要步骤:
1. 初始标记
2. 并发标记
3. 预清理
4. 重新标记
5. 并发清理
6. 并发重置–>注:初始标记与理新标记是独占系统资源的,不能与用户线程一起执行,而其它阶段则可以与用户线程一起执行
(3)设置参数:
-XX:-CMSPrecleaningEnabled 关闭预清理, 不进行预清理, 默认在并发标记后, 会有一个预清理的操作,可减少停顿时间
-XX:+UseConcMarkSweepGC 老年代使用CMS回收器, 新生代使用ParNew回收器
-XX:ConcGCThreads=n 设置并发线程数量,
-XX:ParallelCMSThreads=n 同上, 设置并发线程数量,
-XX:CMSInitiatingOccupancyFraction=n 指定老年代回收阀值, 即当老年代内存使用率达到这个值时, 会执行一次CMS回收,默认值为68, 设置技巧: (Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100)>=Xmn
-XX:+UseCMSCompactAtFullCollection 开启内存碎片的整理, 即当CMS垃圾回收完成后, 进行一次内存碎片整理, 要注意内存碎片的整理并不是并发进行的, 因此可能会引起程序停顿
-XX:CMSFullGCsBeforeCompation=n 用于指定进行多少次CMS回收后, 再进行一次内存压缩
-XX:+CMSParallelRemarkEnabled 在使用UseParNewGC 的情况下, 尽量减少 mark 的时间
-XX:+UseCMSInitiatingOccupancyOnly 表示只有达到阀值时才进行CMS回收
3.2, Class的回收(永久区的回收)
设置参数:
-XX:+CMSClassUnloadingEnabled 开启回收Perm区的内存, 默认情况下, 是需要触发一次FullGC
-XX:CMSInitiatingPermOccupancyFraction=n 当永久区占用率达到这个n值时,启动CMS回收, 需上一个参数开启的情况下使用
4, G1回收器(jdk1.7后全新的回收器, 用于取代CMS)
(1)特点:
–独特的垃圾回收策略, 属于分代垃圾回收器,
–使用分区算法, 不要求eden, 年轻代或老年代的空间都连续
–并行性: 回收期间, 可由多个线程同时工作, 有效利用多核cpu资源
–并发性: 与应用程序可交替执行, 部分工作可以和应用程序同时执行,
–分代GC: 分代收集器, 同时兼顾年轻代和老年代
–空间整理: 回收过程中, 会进行适当对象移动, 减少空间碎片
–可预见性: G1可选取部分区域进行回收, 可以缩小回收范围, 减少全局停顿
(2)G1的收集过程
1. 新生代GC:
2. 并发标记周期:
–初始标记新生代GC(此时是并行, 应用程序会暂停止)–>根区域扫描–>并发标记–>重新标记(此时是并行, 应用程序会暂停止)–>独占清理(此时应用程序会暂停止)–>并发清理
3. 混合回收:
–这个阶段即会执行正常的年轻代gc, 也会选取一些被标记的老年代区域进行回收, 同时处理新生代和年老轻
4. 若需要, 会进行FullGC:
–混合GC时发生空间不足
–在新生代GC时, survivor区和老年代无法容纳幸存对象时,
–以上两者都会导致一次FullGC产生
(3)设置参数:
-XX:+UseG1GC 打开G1收集器开关,
-XX:MaxGCPauseMillis=n 指定目标的最大停顿时间,任何一次停顿时间超过这个值, G1就会尝试调整新生代和老年代的比例, 调整堆大小, 调整晋升年龄
-XX:ParallelGCThreads=n 用于设置并行回收时, GC的工作线程数量
-XX:InitiatingHeapOccpancyPercent=n 指定整个堆的使用率达到多少时, 执行一次并发标记周期, 默认45, 过大会导致并发标记周期迟迟不能启动, 增加FullGC的可能, 过小会导致GC频繁, 会导致应用程序性能有所下降
对象何时进入老年代
三、jvm优化(1)当对象首次创建时, 会放在新生代的eden区, 若没有GC的介入,会一直在eden区, GC后,是可能进入survivor区或者年老代
(2)当对象年龄达到一定的大小 ,就会离开年轻代, 进入老年代, 对象进入老年代的事件称为晋升, 而对象的年龄是由GC的次数决定的, 每一次GC,若对象没有被回收, 则对象的年龄就会加1, 可以使用以下参数来控制新生代对象的最大年龄:
-XX:MaxTenuringThreshold=n 假设值为n , 则新生代的对象最多经历n次GC, 就能晋升到老年代, 但这个必不是晋升的必要条件
-XX:TargetSurvivorRatio=n 用于设置Survivor区的目标使用率,即当survivor区GC后使用率超过这个值, 就可能会使用较小的年龄作为晋升年龄
(3)除年龄外, 对象体积也会影响对象的晋升的, 若对象体积太大, 新生代无法容纳这个对象, 则这个对象可能就会直接晋升至老年代, 可通过以下参数使用对象直接晋升至老年代的阈值, 单位是byte
-XX:PretenureSizeThreshold 即对象的大小大于此值, 就会绕过新生代, 直接在老年代分配, 此参数只对串行回收器以及ParNew回收有效, 而对ParallelGC回收器无效,