什么是垃圾对象?
如何垃圾回收?
何时垃圾回收?
如何判断对象是垃圾对象
方法1:引用计数法
在对象中添加一个引用计数器,当有地方引用这个对象的时候,这个引用计数器的值就+1,当引用失效的的时候
计数就-1 (但是如果堆内存中相互引用,此时就不能进行回收 )
打印垃圾回收简单信息 -verbose:gc
垃圾回收详细信息-XX:+PrintGCDetails
java代码测试如下:
public class Test(){
pirvate Test instance;
public static void main(){
//新建对象
Test t1 = new Test();
Test t2 = new Test();
//内部相互引用
t1.instance = t2;
t2.instance = t1;
//去除外部连接
t1=null;
t2=null;
}
}
方法2:可达性分析法
从gcroot(局部变量表中的引用)开始执行,如果某个对象没有任何引用链关联,则称这个对象是垃圾。
GCRoots的对象:
1:虚拟机栈中的局部变量表
2:方法区中类属性所引用的对象
3:方法区中常量所引用的对象
4:本地方法栈中引用的对象
垃圾回收的算法
方法1:标记清除算法
效率问题:
进行标记清除过后,内存区域中的地址将会不连续,这样的结果就是在创建一个大的对象的时候得要重新触发垃圾回收(此操作比较耗时);
空间问题:
内存区域中的地址将会不连续,寻址过程比较耗时
方法2:复制算法(解决标记清除算法效率问题)
回忆一下jvm内存结构
线程共享:方法区、堆内存
线程私有:栈 ,本地方法栈,程序计数器
堆:
新生代(垃圾回收主要关注区)
Eden(伊甸园)
Survivor(存活区)
Tenured Gen
老年代
能够提高标记清除算法的效率
复制算法的原理:将内存分为两块,一半是放内存地址,一半是空的,此时被垃圾回收标记的对象被清空,剩余的按照内存地址连续的排放在另一半内存中,以此往复。解决了效率问题。但是这样又引入另一个问题,就是内存结构被对半分了,这样内存区域就小了。为了解决这种问题,就在新生代中有了Eden(占用80%),两块Survivor(各占10%),Tenured Gen分代,垃圾回收后就将其中一个Survivor和Eden中的对象复制到另一个Survivor中,空间浪费只有10%Survivor。如果垃圾回收存活的对象超过10%,此时会有内存担保。
方法3:标记整理算法(针对老年代内存 回收效率比较低,垃圾较少)
标记整理清除算法就是标记整理再清除。
将需要收集的内容整理到一边,不需要的放到另一边。。。最后一并回收
方法4:分代手机算法
针对新生代用复制算法
针对老年代用标记整理算法
垃圾收集器
垃圾收集器比较多,因为java适用的场景比较多,所以不同场景有不同的收集器。
Serial:最基本,发展最悠久的
单线程垃圾收集器
多线程先执行,然后线程暂停,执行垃圾收集Serial,然后收集完成后,线程继 续执行
适用于客户端桌面应用(因为单线程运行,开销比较小)
parnew:是基于Serial多线程垃圾收集器。
parallel(针对新生代内存):达到可控制的吞吐量,吞吐量就是CPU运行用户代码的时间与CPU消耗总时 间的比值。即(执行用户代码时间)/(执行用户代码时间+垃圾回收的时间)
-XX:MaxGCPauseMillis 垃圾收集器最大停顿时间
-XX:GCTimeRatio (0,100)
减少垃圾回收的时间,并不能增大吞吐量,
默认值为99。
g1收集器
优势:
并行与并发
分带收集,直接分割内存
空间整合
可预测的停顿
步骤:
初始标记
并发标记
最终标记
筛选回收
cms