垃圾回收
垃圾回收(Garbage Collection GC) 就是释放垃圾占用的空间,防止内存泄漏,有效的利用内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收
1.定义垃圾:引用计数算法、可达性分析算法
*引用计数算法:是通过在对象头中分配一个空间来保存该对象被引用的次数(Reference Count),如果该对象被其他对象引用就加一,如果删除对象的引用就减一,
当引用计数为0时,该对象就会被回收
引用计数算法是将垃圾回收分摊到整个程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到堆中所有的对象都处理完成.
因此严格上来讲,引用计数算法不属于"Stop-The-World"的垃圾回收机制
缺点:会造成循环引用:1.定义两个对象 2.互相引用 3.置空各自的声明引用
*可达性分析算法:通过一些被称为引用链(GC Roots)的对象作为起点,从这些起点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots
没有任何引用链相连时(即从GC Roots结点到该结点不可达),则证明该对象不可用
成功的解决了循环依赖问题
被标记为不可达对象后还需要筛选,
筛选条件是是否有必要执行finalize方法(没有覆盖finalize或者finalize已被执行则为不必要),
被判为没有必要执行的对象会放入一个队列进行二次标记,除非这个对象与引用链上的任何对象有关联,否则回收
可以作为GC Roots 的对象包括:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中(Native方法)
强引用:垃圾回收器绝不会回收,宁愿抛出OutOfMemoryError,使程序终止,也不回收
软引用:当内存不足时,垃圾回收器才回收
弱引用:与软引用相比具有更短的生命周期.当垃圾回收器扫描到它时,不管内存是否足够,都会回收.
虚引用:随时都会被回收
*如何判断一个类是无用的:1.所有实例都被回收了
2.加载该类的ClassLoader已经被回收
3.该类的java.lang.Class对象没有在任何地方被引用,无法通过发射来访问该类
2.怎样回收垃圾:标记-清除算法、复制算法、标记整理算法、综合的一种
*标记-清除算法:时最基础的一种垃圾回收算法,他分为两部分,先把内存区域中的对象标记,然后再清理.
但是存在一个问题,就是内存碎片
*复制算法:是在标记-清除算法上演化来的,解决了内存碎片的问题.
他将可用内存划分为大小相等的两块,每次只使用其中的一块.
当这一块内存用完了,就将还活着的对象复制到另一块上,然后把已经使用的内存全部清理
缺点:内存缩小为原来的一半,代价高.
*标记-整理算法:标记过程与标记-清除算法一样,不过后续不是对可回收对象进行清理,而是让所有存活的对象像一端移动,再清理掉段边界以外的内存
对内存的变动频繁,需要整理所有存活的对象的引用地址,效率低
*综合:一般把java 堆分为新生代和老年代.新生代中每次垃圾收集时都会有大量的对象死去,所以采用复制算法
老年代中对象存活率高, 没有额外空间分配担保,所以采用标记清理或标记整理算法
java堆分为新生代和老年代.新生代分为Eden和Survivor.Survivor分为From和To两个区域
堆=新生代(1/3)+老年代(2/3) 新生代=Eden(8/10)+From(1/10)+To(1/10)
Survivor分为两块的好处就是永远都有一个Survivor Space是空的
新生代GC(Minor GC):频繁且快
老年代GC(Major GC/Full GC):出现Major GC至少伴随着一次Minor GC,慢
3.垃圾回收器:
*.Serial收集器:是最基本的,历史最悠久的回收器.单线程.它工作时其他线程必须停止(Stop-The-World) 老年代:Serial Old
*.ParNew收集器:其实是Serial的多线程版本
*.Parallel Scavenge收集器:关注的是吞吐量(高效利用cpu) 老年代:Parallel Old
*.CMS收集器:真正意义上第一款并发收集器,让垃圾收集线程与用户线程同时工作
初始标记
并发标记
重新标记
并发清除
缺点:对Cpu资源敏感|无法处理浮动垃圾|使用了"标记-清除算法"会产生内存碎片
*.G1(Garbage First)收集器:整体上是标记-整理算法,局部是复制算法
初始标记
并发标记
最终标记
筛选回收