JVM之对象存活算法和GC算法:
1.对象存活算法:
1.1.引用计数算法:
缺点:不能解决循环引用问题。
循环引用造成的问题:
内存泄漏:内存存在不可被GC的对象。
内存溢出:内存存在不可被GC的对象不断堆积导致即使创建了很小的对象也不能被装入。
1.2.可达性分析算法/根搜索算法:
GC Roots:
1.虚拟机栈的栈帧的局部变量表所引用的对象。
2.本地方法栈JNI所引用的对象。
3.方法区静态变量和常量所引用的对象。
2.什么时候进行垃圾回收?
- 当eden区或者s区不够用。
- Old区空间不够用。
- 方法区空间不够用。
- System.gc();手动通知JVM要回收对象,但回不回收还要看JVM,同System.gc();还会占用操作系统资源,降低效率。
3.GC算法(基于对象存活算法):
3.1.标记/清除算法:
当堆满时,GC线程触发,业务线程停滞(STW,Stop
the work)。
3.1.1标记/清除过程:
标记:遍历所有的GC root,将所有的GC root以及所有的引用(可达)标记。
清除:遍历整个堆将所有的没有标记的清除。
3.1.2.标记/清除算法过程内存变化图解:
3.1.3.STW来源:
假如GC线程和业务线程并行,会发生什么不符合我们预期的情况?
- 用户线程产生了一个新对象并在遍历标记的时候标记了,同时与之前的未标记的对象维持着可达性关系(但该对象之前在遍历标记因没有引用指向而未被标记),就会导致不该回收的被回收了。
- 本来被标记的又未被标记了,这就导致了该回收的不被回收。
分析:所有都是因为GC线程和业务线程并行运行,不断抢占cpu资源互相切换导致的。
解决:并行变串行,即运行GC线程就不运行业务线程,运行业务线程就不运行GC线程。
3.1.4.标记/清除算法缺点:
1.遍历标记整个堆,效率低下(解决方法:分代)。
2.内存碎片化,维护空闲列表,开销大(解决方法:存活对象移动到另一块内存有序排列)。
3.2.标记/整理算法:
标记过程跟标记/清除算法的一样,证明整理是在标记/清除的基础上将标记的对象按序排好,再将剩下的那块区域全部回收。使用场景:不在乎消耗。
3.2.1.标记/整理算法内存变化图解:
3.2.2.标记/整理算法缺点:
效率更低:整理引用地址再排队,还是会形成循环引用。
4.总结:
1.分代原因:不同对象的生命周期不同。
如果说同对象的生命周期按照同一种分配策略来划分的话,会把效率降到最低浪费加到最大。
2.复制算法适合朝生夕暮,不在乎内存,因为一进来就死了。
3.Old区适用标记/整理算法原因:进来的对象只是非常少数,回收时整理引用地址再排队的效率相对就不会很低(对象极少)。
4. GC算法本质上本质上是贫民算法。
5.穷:最求效率避免浪费 健壮性