什么是GC?
Java程序运行期间的变量都会存储在JVM内存中,但是JVM的内存又是有限的所以需要释放无效的内存空间。C/C++等语言需要程序员手动申请、释放空间,JVM帮助程序员回收内存空间的过程就是GC。
GC是需要stop-the-world的,就是暂停应用进行GC,完了再恢复。
GC过程
GC的过程说白了就是两个步骤
1. 找出无效的对象。
2. 清除无效的对象,释放内存空间。
如何判断哪些对象是无效的?
判断对线失效有两种方法,引用计数法和可达性分析法
1. 引用计数法::每个对象都有一个引用计数器,被引用一次则加一,引用失效一次则减一,计数器为0则表示对象失效,可以被回收。
如果两个对象互相引用则计数器永远不会为0,导致内存泄漏。
2. 可达性分析法:有一些比较稳定的对象作为GC-ROOT对象,如果堆中的对象能通过引用到达GC-ROOT对象则代表是有效对象,反之则无效对象,可以被回收
可以作为GC-ROOT对象的有
l 类的静态变量、常量
l 栈帧的局部变量表中引用的对象
l 本地方法栈JNI引用的对象
用白话说就是,静态变量没引用你,常量没应用你,方法里边的局部变量也没医用你,JNI还没引用你,那么你就该被回收了。引用不仅是直接引用,只要可达就算引用。
如何清除对象,回收空间?
1. 标记-清除算法:递归遍历整个堆空间,找出无效对象进行清除,这个算法有两大缺点
a) 遍历整个空间效率低下,stw时间长。
b) GC过后会产生很多内存碎片。
2. 复制算法:将申请的内存空间分成两块,每次只用一块内存A,GC的时候找到活的对象复制到另外一块内存B中,清除内存A的空间。特点:
a) 解决了内存碎片的问题。
b) 算法依然低效
c) 引出新的问题,内存利用率低下(每次只能用一半)。
3. 标记-整理算法:第一步遍历出存活的对象,第二步:将标记好的对象复制到内存的一端,回收以外的空间。特点:
a) 解决了碎片问题。
b) 解决了内存利用率低的问题
c) GC效率依然低下。
4. 分代收集算法:该算法不是什么新算法而是根据场景,灵活运用以上三种算法而产生的算法
a) 少量对象存活:复制算法,只需要复制少量的对象就能完成GC。
b) 大量对象存活:标记-清除或者标记-整理,对象存活率高,复制算法过于浪费内存空间,所以使用其他两种算法。
STOP-THE-WORLD
Stw指的是全局暂停的现象。所有的的java代码停止运行。主要原因是GC
GC为什么要STW?
1. 保证内存清理干净。
2. GC必须在一个能保证一致性的内存快照中进行,GC的时候对象的一用不能还在发生变化。