首页 > 代码库 > JVM那些事儿(二)——垃圾回收
JVM那些事儿(二)——垃圾回收
这节小汪介绍一下jvm的垃圾回收机制,首先我们先提问:
1.为什么要有不同的垃圾算法
2.垃圾回收器要解决的终极目的是什么
3.小汪该如何选择自己的垃圾回收器
一、垃圾回收算法
众所周知,java堆内存的垃圾回收由java虚拟机管理,目前java有几种算法用来解决垃圾回收(以下只介绍最重要的两个算法)
1.1 复制算法
如图所示,复制算法可以说是最直观最简洁的算法了。按照复制算法的思路,内存要分为两块 Eden Survivor区域,Eden有一个,Survivor有两个。
首先,各种对象都在Eden+一个Survivor里
其次,当Eden+一个Survivor满的时候,收集开始时,Eden+一个Survivor的存活对象会copy到另一个Survivor中
最后,清除Eden +一个Survivor
以上就是一次复制算法的过程
复制算法小结:
1.Survivor是用来保存存活对象,为了保证对象能被合理清除和复制,永远会有一个Survivor是空闲的,以用来进行下一次回收。
2.jvm对Eden和Survivor的内存比例默认是8:1
3.copy和清除时 其余线程要等待(仔细想一想,如果其余线程不等待,那复制的过程会严重伤害其余线程所使用的对象,导致线程bug)
优劣势:
1.内存空间闲置:复制算法天生只适合于年轻态,因为年轻态对象朝生夕灭,所以copy的对象并不是很多,Survivor可以分配一个很小的空间(8:1)。但是还是会存在内存闲置(10%的空间),尤其是当每次回收都会存活很多对象的时候,不适合于老年代。
2.copy的瞬间会出现卡顿情况(即所有线程等待),但是其卡顿时间要小于其他算法。(后面会解释为什么)
1.2 标记-整理算法
为解决复制算法会带来产生内存空闲,产生了标记-整理算法。
如图所示 该算法分为以下几步:
1.标记要回收的对象(会产生卡顿时间)
2.将存活对象移动到内存的一侧
3.直接删除掉边界以外的内存空间
其思想是基于标记-清除演化而来。
优劣点
1.不用多余的内存空间
2.回收效率慢,由于需要标记,同时内存要移动位置其执行时间大于复制算法。所以它不适合新生代
综上所述,目前jvm回收器会根据年代的不同使用不同的算法收集,新生代使用复制算法 老年代使用标记-整理算法
二、垃圾回收器
2.1垃圾回收器的终极目的
jvm有多种垃圾回收器,都是基于上述两种算法实现的,辣么多回收器,其终极目的是解决以下两个问题。
1.卡顿时间
2.吞吐量
卡顿时间:cpu停止作业执行收集任务的时间吞吐量:在一定时间内cpu运行代码的时间与总时间的比值,可以理解为cpu在一定时间内有多少时间是用来干活的,而不是“偷懒的”。
卡顿时间和吞吐量其实存在一定得矛盾,这里小汪必须解释清楚。
如果仅仅追求低的卡顿时间,可能带来吞吐量的下降,这是因为低卡顿 使收集的垃圾总量变少了。
举例:如果卡顿1秒可以收集100m 那么当卡顿500ms 可能仅仅能收集40m(因为每次卡顿还要有其他时间的支出) ,这样如果要收集1000m的内存,每次的卡顿时间虽然很少,但是卡顿次数增加了,卡顿时间总和其实是上升的,最终会导致吞吐量的下降。
为了解决上面两个终极问题,sun推出了一系列各有千秋的jvm收集器,接下来小汪会一一介绍
2.2 垃圾回收器列表
如图所示 jvm主要有以下几种收集器
年轻态:
Serial 单线程回收器
ParNew 多线程回收器
Parallel 吞吐量优先回收器(多线程)
Parallel Old 吞吐量优先回收器老年版
Serial Old 单线程回收器老年版
CMS “牛逼的” 高并发低停顿回收器
G1 这才是最牛逼 最高大尚的回收器
介绍各种收集器之前,先说一下个人使用的经验吧。
一般来说,在设置jvm参数时,会有以下两种设置
1.Parallel + CMS 追求吞吐量
2.ParNew + CMS 追求低卡顿
多线程的问题——并发和并行
并行:用户线程停止 多线程并行回收垃圾
并发:用户线程和垃圾回收线程一起办公
多线程的问题——并发会引起低效率
用到多线程 还需要设置回收线程的个数 这个要根据cpu的核数来定,一般的,一个线程会占用一个核,cpu很少的情况下,会导致并发回收时,回收器会抢占部分cpu,而真正跑作业的cpu变少,影响效率。
这里利用CMS收集器举例来说:
CMS收集器默认的回收线程数 = (cpu数量 + 3) / 4 大概占用25%的线程
但是当线程数量很少,比如为2 那么有一个线程专门用来回收,这时大作业的任务都挤在一个线程上,会非常影响效率。
所以小汪认为,如果服务器的线程数量很少,且你的项目又是大数据量体验极强的项目,那不妨设置成单线程回收器,总体来说可以比多线程回收器要好。
好啦,下面小汪开始逐一介绍回收器
2.3 Serial回收器
单线程收集,顾名思义,即在垃圾回收时,独占进程进行回收,作业需要停止
其新生代采用复制算法
老年代采用标记-整理算法
优劣点:
1.业务卡顿时间长,当遇到大规模程序需要频繁gc的时候,严重影响业务的体验。
2.好的方面,利用全部资源专注于垃圾回收,其实收集几百mb的内存 也就需要几十ms 相比多线程,可以缩短垃圾回收,当然前提是垃圾回收不频繁。。。。
小结:简单高效
2.4 ParNew回收器
2.默认开启与cpu数量相同的回收线程
小结:多核无敌
2.5 Parallel回收器
2.6 CMS回收器
2.7 G1回收器
三、选择回收器和相应配置参数
3.1 追求高体验 低卡顿型
我的配置参数:3.2 追求高吞吐量
java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
-XX:+UseParallelGC
-XX:ParallelGCThreads=20JVM那些事儿(二)——垃圾回收