第三篇 JVM垃圾回收算法以及引用类型
JVM垃圾回收算法以及引用类型
一.垃圾回收算法
垃圾回收是java体系最重要的组成部分之一.垃圾回收顾名思义就是把不用的对象给丢弃,释放内存空间.
接下来来看看java垃圾回收机制的理论基础:引用技术法,标记压缩法,标记清楚法,复制算法和分代分区.
1.引用技术法
引用计算法的实现很简单,对于一个对象a,只要有任何一个根对象引用它,那么计数加一,引用失效时,计数减1.为0时,就不能再被引用了.
根对象主要有:
1.栈中引用的对象; 2.类静态属性引用的对象. 3.JN引用的对象
这种方法有两个很严重的问题:
- 无法处理循环引用.
- 每次计算都要加减,影响性能。
所以垃圾回收器不使用这种算法.
2.标记清除法
标记清除法把垃圾分为两个阶段:标记阶段和清除阶段.
在标记阶段,从根结点触发,标记所有可达的对象。因此,未被标记的对象就是垃圾对象.这个实现的最大问题就是空间碎片.
3.复制算法
把内存空间分为两份,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的空间中,之后清除正在使用的内存块.交换两者角色,完成回收.
这种算法的缺点就是浪费空间.
java的新生代串行垃圾回收器中,使用了这种思想 .分成eden,from,to三个部分.
From和to也被称为survivor空间,即幸存者空间.垃圾回收时,eden中存活对象会被复制到to空间,from中正在使用的对象也会被存放到to.to空间满了也会被放到老年代.这个时候eden,from里面的空间就是垃圾对象.
复制算法适合新生代,因为垃圾对象多于存活对象.
4.标记压缩法
复制算法高效是因为,存活对象少,但是在老年代存活对象多,如果还是使用复制算法,那么复制成本很高.
标记压缩法是一种老年代的回收算法.它在标记清除算法的基础上做了一些优化.区别在于标记之后,先将所有存活对象压缩到内存的一端。之后清除边界外所有空间.
5. 分代算法
分代算法,将内存空间根据对象的特点分为几块,根据每块区域的特点使用不同的回收算法。
对于新生代来说,回收的频率很高,但是每次回收的耗时很短,老年代回收频率低,耗时长.为了支持高频率的新生代回收,虚拟机可能使用了一种叫做卡表的数据结构.卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一个区域中所有对象是否持有新生代对象的引用.这样在新生代gc时候,不用花大量时间扫描所有老年代对象, 而是先扫描卡表. 只有标志为1才扫描这个区域,.
6.分区算法
分区算法将整个堆空间分成连续的不同小区间,每个小区间独立使用独立回收.这种算法可以控制一次回收多少个小区间.一般来说.堆空间越大,gc所需要的时间越长.所以每次只回收部分空间可以减少一次gc的停顿.
二.对象的引用类型
对象判断可触及性
1.可触及:根对象可达
2.可复活: 可以在finalize()复活.
3.不可触及: finalize被调用,没有复活.这种状态就会被回收.
public class FinalizeTest {
public static FinalizeTest obj = null;
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
obj = this;
}
public static void main(String[] args) throws Exception {
obj = new FinalizeTest();
obj = null;
System.gc();
Thread.sleep(1000);
if(obj == null){
System.out.println("对象为空");
}else{
System.out.println("对象不为空");
}
System.out.println("第二次gc");
obj=null;
System.gc();
Thread.sleep(1000);
if(obj == null){
System.out.println("对象为空");
}else{
System.out.println("对象不为空");
}
}
}
引用和可触及性的强度
java提供了四种级别引用:强引用,弱引用,软引用,虚引用.
强引用:
String str = new String(“test”);
String str1 = str;
这个两个都是强引用,它们具有以下特点:
强引用可以直接访问目标对象,不会被系统给回收,可能导致内存泄漏
软引用:可以被回收的引用,在内存不足的时候。
使用-xms10m测试
public class SoftReferenceTest {
//占用15字节
public static class User {
public String id = "1";
public String name="dfsdafasdf";
}
public static void main(String[] args) {
User user = new User();
SoftReference<User> test = new SoftReference<User>(user);
user = null;
System.out.println(test.get());
System.gc();
System.out.println("after Gc:"+test.get());
byte[] b = new byte[1024*983*7];
System.gc();
//内存不足,被回收了
System.out.println("after Gc1:"+test.get());
}
}
每一个软引用都可以附带一个引用队列,当对象不可达,就会进入引用队列。
public class SoftReferenceQueueTest {
public static class User {
public String id = "1";
public String name="test";
@Override
public String toString() {
// TODO Auto-generated method stub
return this.id;
}
}
static ReferenceQueue<User> queue = null;
public static class UserReference extends SoftReference<User>{
private String idd;
public UserReference(User referent, ReferenceQueue<? super User> q) {
super(referent, q);
this.idd=referent.id;
}
}
public static class ReferenceThread extends Thread{
@Override
public void run() {
while(true){
if(queue != null){
UserReference obj = null;
try{
obj = (UserReference) queue.remove();
}catch (Exception e) {
e.printStackTrace();
}
if(obj !=null){
System.out.println("remove"+obj.idd);
}
}
}
}
}
public static void main(String[] args) {
Thread t = new ReferenceThread();
t.setDaemon(true);
t.start();
queue = new ReferenceQueue<User>();
User user = new User();
UserReference test = new UserReference(user,queue);
user = null;
System.out.println(test.get());
System.gc();
System.out.println("after Gc:"+test.get());
byte[] b = new byte[1024*983*7];
System.gc();
System.out.println("after Gc1:"+test.get());
}
}
弱引用:发现就回收
一旦一个弱引用被回收,便会加入到一个注册的引用队列中
public class WeakReferenceTest {
public static class User {
public String id = "1";
public String name="dfsdafasdf";
}
public static void main(String[] args) {
User user = new User();
WeakReference<User> test = new WeakReference<User>(user);
user = null;
System.out.println(test.get());
System.gc();
System.out.println("after Gc:"+test.get());
byte[] b = new byte[1024*983*7];
System.gc();
System.out.println("after Gc1:"+test.get());
}
}
虚引用:对象回收跟踪
虚引用是所有引用类型中最弱的。一个持有虚引用的对象,几乎和没有一样.当试图使用虚引用.get方法总是会失败.它必须和引用队列一起使用,用来跟踪垃圾回收过程.
public class PhantomReferenceTest {
public static PhantomReferenceTest obj;
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
//复活这个对象
obj = this;
}
static ReferenceQueue<PhantomReferenceTest> queue = null;
public static class ReferenceThread extends Thread {
@Override
public void run() {
while (true) {
if (queue != null) {
PhantomReference<User> obj = null;
try {
obj = (PhantomReference) queue.remove();
} catch (Exception e) {
e.printStackTrace();
}
if (obj != null) {
System.out.println("remove" + obj);
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new ReferenceThread();
t.setDaemon(true);
t.start();
queue = new ReferenceQueue<PhantomReferenceTest>();
obj = new PhantomReferenceTest();
PhantomReference<PhantomReferenceTest> test = new PhantomReference<PhantomReferenceTest>(obj, queue);
obj = null;
System.gc();
System.out.println("after Gc:");
Thread.sleep(1000);
if(obj == null){
System.out.println("对象为空");
}else{
System.out.println("对象不为空");
}
System.out.println("第二次gc");
obj=null;
System.gc();
Thread.sleep(1000);
if(obj == null){
System.out.println("对象为空");
}else{
System.out.println("对象不为空");
}
}
}