哪些区域需要回收
运行时内存分为5个区域:程序计数器、虚拟机栈、本地方法栈、堆、方法区,这些区域是如何回收的?
1-程序计数器、虚拟机栈、本地方法栈
这3个区域是随着线程而生,随着线程而灭,栈中的栈帧数据随着方法的调用到结束有条不紊的进行着入栈出栈,每一个栈帧中分配多少内部基本上在类结构确定下来(类加载)就已知了。内存分配和回收具有确定性,随着方法的结束或者线程的结束后,内存自然就会被回收了。
2- java堆和方法区
一个接口中的多个实现类需要的内存可能不一样,一个方法的多个分支需要的内存也不一样,我们只有在程序处于执行期间才能知道会创建哪些对象,对这部分的内存分配和回收是动态的,垃圾回收主要关注的是这部分的内存。
判断对象是“生”是“死”?
引用计数算法
java虚拟机没有采用引用计数算法来判断对象是否生存,主要考虑的是它很难解决对象之间的相互循环引用的问题。
可达性分析算法(Reachablity Analysis)
基本思路是:从每一个GC Roots出发找到它所有可达的对象,走过的路径成为引用链,被引用链串起来的就是存活对象;其他的虽然存在引用关系,但是GC Roots不可达的,判定为可回收对象。、
GC Roots :可作为的对象包含以下几种:
- 虚拟机栈(栈帧中本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(Native Method)引用的对象
引用与缓存
为了更细致的管理对象的内存,对象引用进行了扩展,分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference,也叫幽灵引用)。
强引用
-
Object obj = new Object();
这种通过关键字new出来的对象就属于强引用,普遍存在的,只要这种引用还关联着对象,就不会被垃圾回收器回收。 - **内存空间不够时,不会进行垃圾回收,而是直接抛出内存溢出异常 **
软引用
用来描述有用但是并不必需的对象,在系统将要发生内存溢出异常之前,JVM将会把这些对象标记成垃圾数据,并对这些标记的数据进行二次回收,若回收后还是没有足够的内存分配,才会抛出内存溢出的异常。 用法: 用来实现内存敏感的缓存
import java.lang.ref.SoftReference;
class ReferenceTest{
Object obj = new Object();//强引用
SoftReference<Object> sr = new SoftReference<Object>(obj);//关联软引用
// 取消强引用,只保留软引用
obj=null;
pass(...);
//通用的处理,以及softreference的复用模式
if(sr.get() != null){
Obeject getRef = sr.get();
use(getRef);
}else{
Object newObj = new Object();
sr = new SoftReference(newObj);//重建软引用
}
}
- 注意
1.在虚拟机内存空间足够大的时候是不会发生内存回收的,而且GC的优先级最低,只有所有线程都停止时才有可能执行GC(即使使用System.gc()也只是告诉JVM这是一个执行GC的好时机,但是实际上JVM会自己决断是否达到了这个条件,比如没有线程正在执行,这是GC这个守护线程就会执行),因为GC有一定的代价。所以与软引用关联的对象在内存充足时是不会发生的,只有处于内存溢出的边缘才会发生回收操作。
2.虽然与软引用关联的对象可能会被收回,但保存这个软引用关系的SoftReference变量并不一定会被回收,所以出现了ReferenceQueue来管理这些变量。
弱引用
- 弱引用描述的是非必需的对象:被弱引用关联的对象只能生存到下一次GC回收发生之前,当GC工作时,无论当前空间是否足够,都会回收只被弱引用关联的对象。
- 用法:一般会和ReferenceQueue进行关联
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class WeakReferenceTest{
public static void main(Stting[] args) throws Exception{
Object obj = new Object();
//ReferenceQueue的作用:
//当弱引用关联的对象被标记为垃圾,准备回收时,
//会自动把保存弱引用的对象WeakReference放到ReferenceQueue中,等待被处理
//主要目的是:引用关联的对象被回收了,但存储引用的对象没有及时回收会造成
//内存膨胀
ReferenceQueue<Object> rf = new ReferenceQueue<Object>();
WeakReference<Object> wf = new WeakReference<Object>(obj,rf);
WeakReference<Object> wf_q = null;
// poll方法,删除队尾引用并返回
while(wf_q = rf.poll()!= null){
// do something for remove reference
}
}
}
虚引用
- 最弱的一种引用关系,无法通过虚引用来获取对象的实例
- 用途:仅仅是在这个对象被回收时收到一个系统的通知。
总结
对于软引用和弱引用,被GC标记为垃圾准备回收时会清除对应的引用,即get方法返回null,然后放入绑定的引用队列中,理论上此时没有真正被回收,只是没有办法在访问到;但是对于虚引用则是在发生回收时放入队列,也是唯一一种确认对象被回收而加入队列的引用,可以利用这一特性做一些有趣的事。
finalize()越狱
通过一条到墙外的地下通道(finalize)、一列快速列车(F-Queue)的惊险越狱大片
- 可达性分析发现这个对象没有到GC Roots的引用链(快到砍头的时候了),进行第一次标记,如果对象没有覆盖Object的finalize方法,或者已经执行过了一次finalize方法,则这个对象的finalize不会执行,等待下一次GC被回收(蠢蛋和倒霉蛋的组合,一个不知道这个秘密通道,一个越狱失败被抓了回来成为重点照顾对象,没日没夜的毒打,只能等死了)
- 如果覆盖了finalize方法但还没有执行(已经踩好点了,还在做最后的精密计划,天一黑,就行动),将这个对象放到F-Queue中等待执行finalize方法(通过地下秘密通道到达狱外,此时列车也到了,正在排队刷卡上车...)
- 以低优先级执行F-Queue中对象的finalize方法,进行第二次标记,如果finalize中将自己对象的引用与外面的引用链上的对象建立了链接,则将其移出F-Queue,成功逃脱回收(滴,学生卡,上车落座,系好安全带,老司机要飙车了),如果没有建立与引用链的链接,则等待下一次GC被回收(滴,余额不足,请及时充值,司机说穷鬼,没钱还想做快速列车。哎哎哎~,老司机带带我呀,带带我呀,带我呀,我呀,呀...曾经的秋名山车神遗弃在角落。)
进行了两次标记:第一次标记是筛选有哪些finalize方法是需要执行的;第二次标记是哪些finalize方法关联了引用链,将其移出队列。
public class SaveSelfTest {
public static Scofield BreakAway= null;//逃生专列
public static void main(String[] args) throws Exception{
Scofield michael = new Scofield();
michael.sayHi();
//第一次逃脱,成功
michael = null;//置为null,触发回收的条件
System.gc();
Thread.sleep(200);
if(BreakAway != null){
BreakAway.sayHi();
}else{
util.print("ScoField:Please save me,I'll dead next morning!");
}
//第二次逃脱,失败,finalize函数只能最多被执行一次
BreakAway = null;
System.gc();
Thread.sleep(200);
if(BreakAway != null){
BreakAway.sayHi();
}else{
util.print("ScoField:Please save me,I'll dead next morning!");
}
System.exit(0);
}
}
class Scofield{
public void sayHi(){
util.print("Hi, I'm Michael ScoField!");
}
@Override
protected void finalize() throws Throwable{
super.finalize();//Object
util.print("Scofield gets on car!");
SaveSelfTest.BreakAway = this;//关联引用链
}
}
class util{
public static void print(String str){
System.out.println(str);
}
}
/* Output
Hi, I'm Michael ScoField!
Scofield gets on car!
Hi, I'm Michael ScoField!
ScoField:Please save me,I'll dead next morning!
*/
参考链接
http://www.cnblogs.com/jiangyi-uestc/p/5679331.html
http://www.importnew.com/14115.html