2.4 复制算法
复制算法的核心就是,将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。
如果内存的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该方式并且效率比较高,反之,则不适合。
2.4.1 JVM中年轻代内存空间:
在GC开始的时候,对象只会存在于Eden区和名为 “From” 的Survivor区,Survivor区 “To” 是空的
紧接着进行GC,Eden区中所有存活的对象都会被复制到 “To” 区,而在 “From” 区中,仍存活的对象会根据他们的年龄值来决定去向,年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到 “To” 区域中。
经过这次GC后,Eden区和From区已经被清空,这个时候,“From” 和 “To” 会交换他们的角色,也就是新的 “To” 就是上次GC前的 “From” ,新的 “From” 就是上次GC前的 “To”,不管怎样,都会保证名为 To 的 Survivor区域是空的。
GC会一直重复这样的过程,知道 “To” 区被填满,“To” 区被填满之后,会将所有对象移动到年老代中
2.4.2 优缺点
优点:
在垃圾对象多的情况下,效率较高
清理后,内存无碎片
缺点:
在垃圾对象少的情况下,不适用,如:老年代内存
分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低
2.4 分代算法
前面介绍了多种回收算法,没有算法都有自己的优点也有缺点,谁都不能替代谁,所以根据垃圾回收对象的特点进行选择,才是明智的选择。
分代算法其实就是这样,根据回收算法的特点进行选择,在JVM中,年轻代适合使用复制算法,老年代适合使用标记清楚或标记压缩算法。
三、垃圾收集器以及内存分配
上面我们介绍了垃圾回收的算法,还需要有具体的体现,在jvm中,实现了多种垃圾收集器,包括:串行垃圾收集器、并行垃圾收集器、CMS(并发)垃圾收集器,G1垃圾收集器,接下来,我们一个个的了解学习。
3.1、串行垃圾收集器
串行垃圾收集器,是指使用单线程进行垃圾回收,垃圾回收时,只有一个线程在工作,并且java应用中的所有线程都要暂停,等待垃圾回收的完成,这种现象称之为 STW(Stop-The-World)。
对于交互性较强的应用而言,这种垃圾收集器是不能够接收的。
一般在javaweb应用中是不会采用该收集器的。
3.1.1 编写测试代码
// 实现:不断的产生新的数据(对象),随机的去废弃对象(垃圾) public static void main(String[] args) throws Exception { List<Object> list = new ArrayList<>(); while (true){ int sleep = new Random().nextInt(100); if(System.currentTimeMillis() % 2 ==0){ //当前的时间戳,是偶数 list.clear(); }else{ //向List中添加1000个对象 for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_"+i,"value_"+System.currentTimeMillis()+i); list.add(properties); } } Thread.sleep(sleep); } }
3.1.2 设置垃圾回收为串行收集器
在程序运行参数添加2个参数,如下:
-XX:UseSerialGC:指定年轻代和老年代都是用串行垃圾收集器
-XX:PrintGCDetails:打印垃圾回收的详细信息
# 为了测试GC,将堆的初始值和最大内存都谁知为16M
-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m
启动程序后,可以看到下面信息:
[GC (Allocation Failure) [DefNew: 4416K->511K(4928K), 0.0052836 secs] 4416K->1768K(15872K), 0.0053321 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 10943K->10943K(10944K), 0.0186019 secs] 15871K->14611K(15872K), [Metaspace: 3828K->3828K(1056768K)], 0.0186344 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
GC日志信息解读:
年轻代的内存GC前后的大小:
DefNew:表示使用的是串行垃圾收集器
4416K->511K(4928K): 表示,年轻代GC前,占有4416K内存,GC后,占有512K内存,总大小4928K
0.0052836 secs: 表示,GC所用的时间,单位为毫秒
**4416K->1768K(15872K):**表示,GC前,堆内存占有4416K,GC后,占有1973K,总大小为 15872K
Full GC: 表示,内存空间全部进行GC