1、分代算法
分代算法将内存区间根据对象的特点分为几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率;
JVM会将所有的新建对象放入新生代的内存区域,新生代的对象90%会很快被回收。新生代比较适合使用复制算法;老年代的对象都是经过几次回收后存活下来的。因此老年代采取标记压缩或者标记清除算法。
新生代回收的频率很高,但是每次耗时都很短,老年代回收频率低但是消耗时间多。虚拟机使用卡表的数据结构,是一个比特位集合,每一个集合位表示老年代的某一个区域的所有对象是否持有新生代对象的引用。
这样在新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,可以先扫描卡表,只有卡表的标记位为1时才需要扫描。
分区算法
2、分区算法
分代算法按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间。可以一次控制回收多少个小区间;
堆空间越大,一次GC所需的事件越长,产生的停顿越长。将一块大的内存分割成多个小块根据目标的停顿时间合理地回收若干个小区间,而不是整个堆空间从而减小一次GC的停顿;
3、判断可触及性
- 可触及的:从根节点开始可以到达的对象;
- 可复活的:对象的所有引用都被释放,可能在finalize()函数中复活;
- 不可触及的:finalize()函数被调用,并且没有复活,就会进入不可触及状态,不可触及的对象不能被复活,因为finalize函数只被调用一次;
public class CanReliveObj {
public static CanReliveObj obj;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj=this;
}
@Override
public String toString() {
return "I am CanReliveObj";
}
public static void main(String[] args) throws InterruptedException {
obj=new CanReliveObj();
obj=null;
System.gc();
Thread.sleep(1000);
if(obj==null) System.out.println("obj is null...");
else
System.out.println("obj 可用");
System.out.println("第二次GC");
obj=null;
System.gc();
Thread.sleep(1000);
if(obj==null) System.out.println("obj is null...");
else
System.out.println("obj 可用");
}
}
//运行结果
CanReliveObj finalize called
obj 可用
第二次GC
obj is null...
在第一次GC时,在finalize函数调用之前,虽然系统的引用已经被清除,但是finalize方法中对象的this引用依然会被传入方法内部,对象就会复活。此时对象又变为可触及状态。而finalize只会被调用一次。在第二次清除对象时,对象无机会复活因此会被回收;
4、引用和可触及的强度
4.1 强引用
- 强引用的对象是可触及的,不会被回收;
- 软引用、弱引用、虚引用的对象是软可触及、弱可触及、虚可触及的;
强引用可以直接访问目标对象;
强引用指向的对象在任何时候都不会被系统回收;
强引用可能会导致内存泄漏;
4.2 软引用
一个对象只持有软引用,当堆空间不足时就会被回收;
public class SoftRef {
public static class User{
public int id;
public String name;
public User(int id,String name){
this.id=id;
this.name=name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public static void main(String[] args) {
User u=new User(1,"gyx");
SoftReference<User> userSoftRef = new SoftReference<>(u);
u=null;
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After gc:");
System.out.println(userSoftRef.get());
byte[] b=new byte[1024*925*7];
System.gc();
System.out.println(userSoftRef.get());
}
}
u变量为强引用,通过强引用u建立软引用,在21行取出强引用。23行从软引用这获取强引用对象,24行进行一次垃圾回收;
GC未必会回收软引用的对象,当内存资源紧张时软引用对象会被回收,所以软引用对象不会因此内存溢出;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
class User {
public int id;
public String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
class UserSoftReference extends SoftReference<User>{
int uid;
public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
super(referent, q);
this.uid = referent.id;
}
}
public class SoftRefQ {
static ReferenceQueue<User> softQueue=null;
public static class CheckRefQueue extends Thread{
@Override
public void run() {
while(true){
if(softQueue!=null){
UserSoftReference obj=null;
try {
obj=(UserSoftReference)softQueue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (obj!=null)
System.out.println("user id"+obj.uid+" is deleted.");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t=new CheckRefQueue();
t.setDaemon(true);
t.start();
User u=new User(1,"gyx");
softQueue=new ReferenceQueue<>();
UserSoftReference userSoftRef = new UserSoftReference(u, softQueue);
u=null;
System.out.println(userSoftRef.get());
System.gc();
System.out.println("after gc:");
System.out.println(userSoftRef.get());
System.out.println("try to create byte array and gc:");
byte[] b=new byte[1024*940*7];
System.gc();
System.out.println(userSoftRef.get());
Thread.sleep(1000);
}
}
//运行结果
User{id=1, name='gyx'} //(第一次从软引用获得对象)
after gc:
User{id=1, name='gyx'}//(gc后,软引用对象没有回收)
try to create byte array and gc://(创建大数组)
user id1 is deleted.//(引用队列探测到对象被删除)
null//(对象已被回收,无法通过软引用获取对象)
当对象的可达性状态发生变化时(由可达变为不可达),软引用对象就会进入引用队列,通过这个引用队列可以跟踪对象的回收情况;
上述代码中,67行进行内存不够进行垃圾回收将u回收掉,因此userSoftRef会进入引用队列,此时监控到引用队列不空表示发生了对象回收;
4.3 弱引用
在系统GC时,只要发现弱引用,不管堆空间使用情况如何都会进行回收;由于垃圾回收器的线程优先级通常很低,因此不一定能很快地发现持有弱引用的对象。一旦一个弱引用对象被垃圾回收器回收便会加入到一个引用队列中;
public class WeakRef {
public static void main(String[] args) {
User u=new User(1,"gyx");
WeakReference<User> userWeakRef=new WeakReference<>(u);
u=null;
System.out.println(userWeakRef.get());
System.gc();
System.out.println("after gc:");
System.out.println(userWeakRef.get());
}
}
//运行结果
User{id=1, name='gyx'}
after gc:
null
软引用、弱引用都非常适合保存那些可有可无的缓存数据。当系统内存不足时缓存数据会被回收、不会导致内存溢出;当内存资源充足时,又可以存在比较长的时间从而起到加速系统的作用。
4.4 虚引用(对象回收跟踪)
试图通过虚引用的get获得强引用时总是失败。一般虚引用和引用队列一起使用跟踪垃圾回收过程;
当垃圾回收器准备回收一个对象时,如果发现他还有虚引用就会在回收对象后将虚引用加入到引用队列,通知应用程序对象的回收情况;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class TraceCanReliveObj {
public static TraceCanReliveObj obj;
static ReferenceQueue<TraceCanReliveObj> phantomQueue=null;
public static class CheckRefQueue extends Thread{
@Override
public void run() {
while(true){
if(phantomQueue!=null){
PhantomReference<TraceCanReliveObj> objt=null;
try {
objt=(PhantomReference<TraceCanReliveObj>)phantomQueue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (objt!=null)
System.out.println("TraceObj is deleted.");
}
}
}
}
@Override
public String toString() {
return "i am CanReliveObj";
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("canReliveObj finalize called");
obj=this;
}
public static void main(String[] args) throws InterruptedException {
Thread t=new CheckRefQueue();
t.setDaemon(true);
t.start();
phantomQueue=new ReferenceQueue<>();
obj=new TraceCanReliveObj();
PhantomReference<TraceCanReliveObj> phantomRef=new PhantomReference<>(obj,phantomQueue);
obj=null;
System.gc();
Thread.sleep(1000);
if(obj==null) System.out.println("obj is null");
else System.out.println("obj is avaiable");
System.out.println("第二次gc");
obj=null;
System.gc();
Thread.sleep(1000);
if(obj==null) System.out.println("obj is null");
else System.out.println("obj is avaiable");
}
}
//
canReliveObj finalize called
obj is avaiable
第二次gc
TraceObj is deleted.
obj is null