在上节介绍过,其中堆中存储几乎所有的对象实例,如上图(这里没有PermGen,因为JDK8以后已经取消了永久代)。
【简单过程】对象是有生命周期的,new一个对象时,会被分配到eden区域,当eden满了以后触发一次Minor GC,仍生存的对象被复制到from区域,当再次GC时,对象从from被复制到to,这个时候from和to的角色互换了,以后再GC,对象会又被辅助到from,来来回回,每次对象的代龄都+1,当累加到一定值(默认18)后,对象还存活的话,被复制到old。
这里有一个区别,如果是大对象,直接进入old,不执行来来回回的过程。
那么如何判定这个对象是不是还活着呢?java采用的是可达算法(除此之外还有计数算法),如果该对象有到达GC Roots的引用路径,就认为是活的,可作为GC Roots的对象包括:
1. 栈中引用的对象;
2. 方法区类静态属性引用的对象;
3. 方法区常量引用的对象;
引用路径根据强弱又分为四种(算上 JNI Weak Reference 就是五种):强引用(Strong Ref)>软引用(Soft Ref)>弱引用(Weak Ref)>虚引用(Phantom Ref)
1. 强引用:比较常见,如 Student stu=new Student();这个类就是强引用;
2. 软引用:一般在内存不够时,会被回收,这种引用可有可无,使用SoftReference实现;并不是每次GC都被回收,只有在内存不足时,才根据LRU策略回收。
3. 弱引用:也是可有可无的引用,一旦遇到GC就被回收,使用WeakReference实现。
4. 虚引用:最弱的引用,也是形同虚设的引用,唯一的目的是对象被GC时接收系统通知,使用PhantomReference实现。
当GC时,如果判断该对象和GC Roots没有可达路径,也不是立即被回收,过程如下:
首先判断是否可达,如果不可达,第一次标记该对象,标记后会判断该对象要不要执行一次finalize()方法;然后把对象放入F-Queue队列中,等待再次标记;GC对F-Queue中的对象再做一次标记,这个时候如果发现某对象又可达了,则被移出F-Queue队列,不再回收,否则被最终回收掉。
所以如果一个对象不想被回收,在GC的第二次标记时,还是有机会再抢救一次,比如把自己赋给某个类的变量等等,这也是为什么有时候看到一个对象的finalize()方法被执行了,但是没有回收的原因。另外说到finalize()方法,因为对象的该方法只会被执行一次,所以只有在第一次被GC时可以在这个方法内抢救自己一次,以后再被GC时该方法不会再执行,也就没办法抢救自己了,只能任命了。
来源:oschina
链接:https://my.oschina.net/ag47/blog/4436584