盘点了常考的Java GC

知识图谱

自动化解决了:√

第一个对象内存分配的问题。

第二个回收分配给对象的内存的问题

活动于Java 堆

对象被判定为垃圾的标准: √

没有被其他对象引用

判定方法

引用计数法 √

Ø 通过判断对象的引用数量来决定对象是否可以被回收

Ø 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1

Ø 任何引用计数为0的对象实例都可以被当作垃圾收集

Ø 优点:执行效率高,程序执行受影响较小

Ø 缺点:无法检测出循环引用的情况,导致内存泄漏

实例:

循环引用

循环引用

可达性分析算法(主流)√

通过判断对象的引用链是否可达来决定对象是否可以被回收

GCroot

可以作为GC Root的对象 -

Ø 虚拟机栈中引用的对象(栈帧中的本地变量表)

Ø 方法区中的常量引用的对象

Ø 方法区中的类静态属性引用的对象

Ø 本地方法栈中JNI(Native方法)的引用对象

Ø 活跃线程的引用对象

垃圾回收算法

标记-清除算法(Mark and Sweep)-

Ø 标记:从根集合进行扫描,对存活的对象进行标记

Ø 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存

缺点:

Ø 碎片化

由于标记清除不需要进行对象的移动并且仅对不存活的对象进行处理,因此标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大的对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收工作。

复制算法(Copying)-

Ø 分为对象面和空闲面

Ø 对象在对象面创建

Ø 存活的对象被从对象面复制到空闲面

Ø 将对象面所有对象内存清除

优点:

Ø 解决碎片化问题

Ø 顺序分配内存,简单高效

Ø 适用于对象存活率低的场景

缺点:

复制收集算法在应对对象存活率较高的情况时,要进行较多的复制操作,效率将会变低。如果不想浪费百分之50的空间,就要有额外空间分配担保以应对被使用的内存中所有对象都存活的极端情况。(在老年代不能直接使用这种算法)

标记-整理算法(Compacting)比较适用于老年代的垃圾回收 -

Ø 标记:从根集合进行扫描,对存活的对象进行标记

Ø 清除:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。

特点

Ø 避免内存的不连续行

Ø 不用设置两块内存交换

Ø 适用于存活率高的场景

分代收集算法(Generational Collector)√

Ø 垃圾回收算法的组合拳

Ø 按照对象生命周期的不同划分区域以采用不同的垃圾回收算法

Ø 目的:提高JVM的回收效率

GC的分类

Minor GC √

发生在年轻代中的垃圾收集工作,所采用的是复制算法,年轻代几乎是所有java对象出生的地方(即java内存的申请和存放都是在这个地方),java中的大部分对象通常不需要长久的存活,因此新生代是GC收集垃圾的频繁区域。

Full GC √

由于老年代的垃圾的回收一般会伴随着年轻代的垃圾收集,因此称为Full GC

年轻代:尽可能快速地收集掉那些生命周期短的对象 √

Ø Eden区(新的对象首先放入Eden区,如果放不下可能放到survivor甚至是老年代中)

Ø 两个Survivor区(分为From区和To区)

如果对象在Eden出生,并且被挤满:

会触发一次Minor GC,如果此时对象存活,就会被复制到一块survivor区中

我们假设复制到s0中,称s0为from区域,清理所有使用过的Eden区域,并将存活对象的年龄设置成1.

假设Eden区再次填满

再次触发Minor GC,然后将Eden中和S0中的存活对象拷贝到s1中并将年龄+1

此时s1从to区变成了from区,s0从from区变成了to区。拷贝完成后Eden和s0均被清空,这样便完成了第二次Minor GC

假设此时Eden区又满了,触发了第三次Minor GC。

当对象年龄达到某个值时这些对象会成为老年代(默认是15岁),也可以用-XX:MaxTenuringThreshold来调整。这也不是一定的,对于一些较大的对象即需分配一块较大的连续空间的对象就会进入到老年代。

对象如何晋升到老年代 √

Ø 经历一定Minor次数依然存活的对象

Ø Survivor区中存放不下的对象

Ø 新生成的大对象(-XX:+PretenuerSizeThreshold)

常用的调优参数

Ø -XX:SurvivorRatio: Eden和Survivor的比值,默认8:1

Ø -XX:NewRatio:老年代年轻代内存大小的比例 1:2

Ø -XX:MaxTenuringThreshold:对象从年轻代晋升到老年代经过GC次数的最大阈值

使用-Xms和-Xmx来设置老年代和年轻代内存大小

Full GC和Major GC(单指老年代或者等价于Full GC 看其他人怎么定义)

Full GC比Minor GC慢得多,但是执行频率低

触发Full GC的条件(3点即可) √

Ø 老年代空间不足

Ø 永久代空间不足

Ø CMS GC时出现promotion failed, concurrent mode failure

Ø Minor GC晋升老年代的平均大小大于老年代的剩余空间

Ø 调用System.gc();

Ø 使用RMI来进行RPC或管理的JDK应用,每小时执行一次Full GC

Promotion failed是在进行minor GC的时候Survivor space放不下,对象只能放入老年代,但是此时老年代也放不下。

Concurrent mode failure是在进行CMS GC的时候同时有对象要放入老年代中,而此时老年代空间不足。

触发Minor GC的条件 -

当eden区满了

Stop-the-World √

Ø JVM由于要执行GC而停止了应用程序的执行

Ø 任何一种GC算法中都会发生

Ø 多数GC优化通过减少Stop-the-world发生的时间来提高程序性能

Safepoint -

安全点就是指,当线程运行到这类位置时,堆对象状态是确定一致的,JVM可以安全地进行操作,如GC,偏向锁解除等。

Ø 分析过程中对象引用关系不会发生变化的点

Ø 产生Safepoint的地方:方法调用;循环跳转;异常跳转等

Ø 安全点数量得适中

常见的垃圾收集器

除了CMS都是 年轻代复制, 老年代标记整理

JVM的运行模式:√

Ø Server

Ø Client

Server启动慢,稳定后运行块

Client启动快。

使用java –version就能看到运行在哪个模式下

垃圾收集器之间的联系

有连线代表可以结合使用。

年轻代常见垃圾收集器

Serial收集器 √

ParNew收集器 √

Parallel Scavenge收集器 √

吞吐量=(运行用户代码时间)/(垃圾收集时间+运行用户代码时间)

老年代常见垃圾收集器

Serial Old收集器 √

Parallel Old收集器 -

CMS收集器 -

资源消耗大,尽可能缩短停顿时间,适用于存活时间长的对象。

G1收集器 -

使用 –XX:+UseG1GC

使用:复制+标记-整理算法

Garbage First收集器的特点

Ø 并行和并发

Ø 分代收集

Ø 空间整合

Ø 可预测的停顿

G1收集器的运作大致可划分为以下几个步骤:

1、初始标记(Initial Making)

2、并发标记(Concurrent Marking)

3、最终标记(Final Marking)

4、筛选回收(Live Data Counting and Evacuation)

G1和Parallel Scavenge都没用传统的GC框架而是独立实现的。

Object的finalize()方法是否与C++的析构函数作用相同 -

Ø 与C++的析构函数不同,析构函数调用确定,而它的是不确定的

Ø 将未被引用的对象放置于F-Queue队列

Ø 方法执行随时可能被终止

Ø 给予对象最后一次重生的机会

其实是不符合预期的(还没有执行finalization=this的操作)因此加上sleep

为对象创造一次重生的机会。但是不确定性较大,无法保证各对象的调用顺序,同时运行代价相当高昂,不建议使用finalize方法。

F已经是空指针了,但是f指向的对象还是有finalization指向的。

Java中的强引用,软引用,弱引用,虚引用有什么用

强引用(Strong Reference)√

Ø 最普遍的引用:Object obj=new Object()

Ø 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象

Ø 通过将对象设置成null来弱化引用,使其被回收

软引用(Soft Reference)√

Ø 对象处在有用但非必须的状态

Ø 只有当内存空间不足时,GC会回收该引用的对象的内存

Ø 可以用来实现高速缓存

1
2
String str = new String("abc");// 强引用
SoftReference<String> softReference = new SoftReference<>(str);//软引用

还可以配合引用队列使用

弱引用(Weak Reference)√

Ø 非必须的对象,比软引用更弱一些

Ø GC时会被回收

Ø 被回收的概率也不大,因为GC线程优先级比较低

1
2
String str = new String("abc");// 强引用
WeakReference<String> weakReference = new WeakReference<>(str);//弱引用

虚引用(PhantomReference)-

Ø 不会决定对象的生命周期

Ø 任何时候都可能被垃圾收集器回收

Ø 跟踪对象被垃圾收集器回收的活动,起哨兵作用

Ø 必须和引用队列ReferenceQueue联合使用

1
2
3
String str = new String("abc");// 强引用
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<String> ref = new PhantomReference<>(str,queue);//弱引用

引用队列ReferenceQueue -

Ø 无实际存储结构,储存逻辑依赖于内部节点之间的关系来表达

Ø 存储关联的且被GC的软引用,弱引用和虚引用

节点是reference本身,queue是类似链表的结构