Java引用的种类
对于JVM的垃圾回收机制来说,如果一个对象,没有一个引用指向它,那么它就被认为是一个垃圾。那该对象就会回收。可以把JVM内存中对象引用理解成一种有向图,把引用变量、对象都当成有向图的顶点,将引用关系当成图的有向边(注意:有向边总是从引用变量指向被引用的Java对象)
1、可达状态
当一个对象被创建后,有一个或一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可以通过引用变量来调用该对象的方法和属性。
GcRoot的种类
1.虚拟机栈:栈帧中的本地变量表引用的对象
2.native方法引用的对象
3.方法区中的静态变量和常量引用的对象
2、可恢复状态
如果程序中某个对象不再有任何引用变量引用它,它就进入了可恢复状态,在这个状态下,系统的垃圾回收机制准备回收该对象所占用的内存空间,在回收该对象之前,系统会调用所有对象的finalize方法进行资源的清理,如果系统在调用finalize方法重新让一个引用变量引用该对象,则这个对象会再次变为激活状态,否则该 对象状进入不可达状态。
3、不可达状态
当对象与所有引用变量的关联都被切继,且系统已经调用所有对象的finalize方法依然没有该对象变成可达状态,那这个对象将永久性地失去引用,最后变成不可达状态,只有当一个对象处于不可达状态时统才会真正回收该对象所占有的资源。
四种引用
强引用
强引用是Java编程中使用广泛的引用类型,被强引用所引用的Java对象绝不会被垃圾回收,当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题,因此强引用是造成Java内存泄漏的主要原因之一
class Person
{
String name;
int age;
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class ReferenceTest {
public static void main(String[] args) {
//创建一个长度为10000的强引用数组,来保存10000个Person对象
Person[] person=new Person[10000];
//依次初始化
for(int i=0;i<person.length;i++)
{
person[i]=new Person("名字"+i,(i+1)*2%100);
}
System.out.println(person[1]);
System.out.println(person[3]);
//通知系统进行垃圾回收
System.gc();
System.runFinalization();
System.out.println(person[1]);
System.out.println(person[3]);
}
}
我们来把修改java虚拟机内存,把堆内存减少到2m
(操作: -Xmx2m -Xms2m )
软引用
软引用通过SoftReference类来实现,当系统内存空间足够,软引用的对象不会被系统回收,程序也可以使用该对象,当系统内存不足时,系统将会回收
// 创建一个长度为10000的弱引用数组,来保存10000个Person对象
SoftReference<Person>[] person = new SoftReference[10000];
// 依次初始化
for (int i = 0; i < person.length; i++) {
person[i] = new SoftReference<Person>(new Person("名字" + i, (i + 1) * 2 % 100));
}
堆内存减少到2m,再运行
弱引用
弱引用与软引用有点相似,区别是弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
//创建一个字符串对象
String str=new String("疯狂Java讲义");
//创建一个弱引用,让该弱引用引用到“疯狂Java讲义”字符串对象
WeakReference<String> wr=new WeakReference<String>(str);
//切断str引用变量和“疯狂Java讲义”字符串对象之间的引用关系
str=null;
//取出弱引用所引用的对象
System.out.println(wr.get());
//强制垃圾回收
System.gc();
System.runFinalization();
//再次取出弱引用所引用的对象
System.out.println(wr.get());
虚引用
虚引用通过PhantomReference类实现,类似没有引用,主要作用是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含指定的虚引用,从而了解虚引用所引用的对象是否即将被回收,虚引用不能单独使用,必须要和引用队列(ReferenceQueue)联合使用
//创建一个字符串对象
String str=new String("这是虚引用");
//创建一个引用队列
ReferenceQueue<String> rq=new ReferenceQueue<String>();
//创建一个虚引用,让该虚引用引用到“这是虚引用”字符串对象
PhantomReference<String> pr=new PhantomReference<String>(str,rq);
//切断str引用与"这是虚引用"字符串之间的引用
str=null;
//取出虚引用所引用的对象
System.out.println(pr.get());
//强制垃圾回收
System.gc();
System.runFinalization();
//取出引用队列中最先进入队列中引用与pr进行比较
System.out.println(rq.poll()==pr);
从运行结果可以看出,在未强制进行垃圾回收,程序输出null,说明系统无法通过虚引用访问被引用的对象,当程序强制回收垃圾后,虚引用引用的对象被回收,然后该引用会添加到关联的引用队列中,所以输出true,所以说程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收
Java的内存泄漏
无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费,就是内存泄漏
1、静态变量引起内存泄露。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
3、监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
5、内部类和外部模块等的引用
6、单例模式