如何判断一个对象是否为垃圾?
没有使用的对象视为被回收,其中常见的是可达性分析
(可以解决循环引用的问题),定义GC ROOTS根节点,然后判断对象与根节点之间是否可达,如果不可达则是垃圾要被回收;垃圾回收的主要区域是堆和方法区,这两个区域是线程共享,就是java中所有的线程都可以访问;垃圾回收也是为了释放内存。
可达性分析:
- gc roots根节点:在对象的引用中,会有几种对象变量:来自线程栈中的局部变量表中的变量、静态变量、本地方法栈中的变量,这些都被称为gc roots根节点。
- 判断依据:gc在扫描空间中的而某个节点时,向上遍历,看看能不能遍历到gc roots根节点,如果不能,那么意味着这个对象是垃圾。
对象中的finalize方法
Object类有一个方法,也就是任何一个对象都有finalize方法。这个方法是对象被回收之前的最后一根救命稻草。
- GC在垃圾对象回收之前,先标记垃圾对象,被标记的对象的finalize方法被调用
- 电泳finalize方法如果被对象调用,则进行第二次标记,被标记的对象会被从回收的集合中移除出来,继续存放
- 调用finalize方法如果对象没有被引用,那么将会回收
- 注意finalize方法只是用一下,第二次直接回收。
对象逃逸分析
什么是对象逃逸分析?
对象逃逸分析就是分析对象的动态作用域,当一个对象在一个方法中被定义之后,它很有可能被外部方法所引用,例如作为调用参数传递到其他地方中。
public User test1{
User user=new user();
user.setId(1)
user.setName("张三")
return user;
}
public void test2{
User user=new user();
user.setId(1);
user.setName("张三");
}
这种对象没有被外部访问,且在堆空间上频繁创建,当方法结束,需要被gc,浪费了性能。
所以在1.7之后,就会进行一次逃逸分析(默认开启),于是这样的对象就直接在栈上创建,随着方法的出栈而被销毁,不需要进行
在栈上分配内存的时候∶会把聚合量替换成标量,来减少栈空间的开销,也为了防止栈上没有足够连续的空间直接存放对象。
- 标量:java中的基本数据类型(不可再分)
- 聚合量:引用数聒尖。
垃圾回收算法
-
标记–清除算法
-
复制算法
- 标记–整理算法
- 分代回收算法
堆空间分为新生代 和老年代,其中新生代占2/3比例,老年代占1/3,其中新生代又分为伊甸园区、幸存区1、幸存区2。
流程:
1、伊甸园区产生新的对象,年龄为1,当伊甸园满区时候,进行Minor GC算法,开始回收垃圾对象,然后存活的对象进入到幸存区1(Survivor1),存货对象年龄+1变为2。
2、当伊甸园区产生新的对象为满时,再次进行Minor GC算法,伊甸园区存活的对象和幸存区存活的对象一起进入幸存区2,对象年龄+1,此时伊甸园区和幸存区1为空。
3、伊甸园区(Eden)再次产生新的对象为满时候,开始Minor GC算法,进行垃圾回收,伊甸园区和幸存区2的存活的对象一起进入到幸存区1,对象的年龄+1;此时伊甸园区和幸存区2为空。
4、一次类推,当对象的年龄达到15后,对象进入到老年代区,反复以此,老年代区的对象为满的时候进行full GC算法,进行垃圾清理。
-
新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。
-
老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。
什么样子的对象进入到老年生代?
- 大对象直接进入到老年代:大对象可以设置参数调配大小
- 当对象的年龄达到15岁时将进入老年代,这个年龄可以通过参数设置
- 更具对象动态年龄判断,如果s区(幸存区)中的对象总和超过s区的中50%,那么下一次做复制到饿时候,把年龄大于等于这次最高年龄对象都一次性放入到老年代。
- 老年代空间分配担保机制