JVM系统优化实践(5):什么时候GC以及有哪些GC

时间:2023-02-27 21:59:22

您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~




既然程序运行会产生大量的废弃物,也就是「垃圾」,那总不能一直堆着不管吧。现在就来粗浅地谈谈Java里面什么时候会触发GC以及有哪些GC。

通过之前的电商支付系统,可以知道系统运行创建的对象都是优先分配在JVM的年轻代中的,年轻代里面的对象越来越多快满的时候就会触发垃圾回收机制。

这就涉及到一个「可达性分析法」,也就是判定哪些对象可以被回收,哪些不能被回收:只要某个对象被局部变量引用,就说明该对象有一个GC Roots,此时就不能被回收了。

所谓GC Roots,在一般情况下指的是方法的局部变量和静态变量。只要对象被方法的局部变量、静态变量给引用了,它就不能被回收了,至少不能被马上回收。

Java对象有几种不同的引用类型:

1、强引用:一个变量显式地引用一个对象;

2、软引用:对一个匿名内部类或者对引用的引用,例如:

// 强引用

String str = new String(“abc”);

// 软引用

SoftReference<String> softref = new SoftReference<String>(str);

// 也是软引用

SoftReference<String> softref = new SoftReference<String>(new String(“abc”));

3、弱引用:只具有弱引用的对象拥有更短暂的生命周期,例如:

// 弱引用

WeakReference<String> weakref = new WeakReference<String>(new String(“abc”));

str = null;

4、虚引用:顾名思义,就是形同虚设,实际中很少使用,可以不管。


再来看看有哪些GC方法。

年轻代的内存被分为两部分,其中一块创建对象实例,供栈中的局部变量引用。当空间不足时,就可能触发年轻代的垃圾回收:

JVM系统优化实践(5):什么时候GC以及有哪些GC


由于一瞬间回收掉大量垃圾对象时,可能会造成大量的空白内存区域,大小不一,非常凌乱,造成大量的内存浪费,因此不能直接清除垃圾对象而保留存活对象。所以合理的做法就是复制(复制算法):

1、先保留存活对象;

2、再将存活对象转移(复制)到另一块空白内存中;

3、最后将原区域整体回收;

4、两块区域循环往复着使用。

JVM系统优化实践(5):什么时候GC以及有哪些GC


这就是GC当中最最简单、最最基础的复制算法。

它的缺点是:空间使用效率只有50%。从图上可以明显看得出来,比较浪费,空间越大越浪费。

优化:将年轻代划分为三块:

1、1个Eden区:占80%年轻代空间;

2、2个Survivor区:S0、S1各占10%年轻代空间;

平常情况下可以使用的,就是Eden区和其中一块Survivor区(比如S0):

JVM系统优化实践(5):什么时候GC以及有哪些GC


如果Eden区快满了,就会触发Minor GC:

1、Minor GC时,Eden中的存活对象会一次性转移到另一块空闲的Survivor区中区(比如S1);

2、Eden区被清空,然后被再次分配新对象;

3、如果Eden再满,再次触发Minor GC;

4、将Eden和S1中的存活对象转移到S0中去;

5、如此循环往复。


对象不可能永远年轻,总会慢慢变老。自然世界用光阴让人两鬓斑白,JVM则默默记住对象的「劫数」,并以此决定它的生死:

对象每在年轻代“躲过”一次GC而被转移到另一个Survivor时,对象“年龄”就会增加“一岁”(被标记一次),默认是被标记15次之后,就会被转移到老年代,可通过-XX:MaxTenuringThreshold参数设置。

JVM系统优化实践(5):什么时候GC以及有哪些GC


当然计算机世界还有自己的规则——除了被标记,还有另一个规则可让对象进入老年代——如果当前Survivor区中,年龄相同的一批对象总大小 ≥ Survivor × 50%,那么这批对象及比它们年龄更大的对象,就都直接进入老年代。例如:如果年龄1 + 年龄2 + 年龄3 ≥ Survivor × 50%,那么大于等于年龄3的所有对象都进入老年代。

JVM系统优化实践(5):什么时候GC以及有哪些GC


计算机的规则只有工程师可以干预:通过设置参数XX:PretenureSizeThreshold的值,可以直接将大对象放到老年代,根本就不经过年轻代。

工程师就是计算机的上帝!




感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~