通过关键字new创建Java对象,可视作为Java对象申请内存空间,JVM会在堆内存中为每个对象分配空间;
当一个Java对象失去引用时,JVM的垃圾回收机制会自动清除它们,并回收它们所占的内存空间。
Java内存管理包括内存分配(创建Java对象时)和内存回收两个方面(回收Java对象时)。
对于Jvm的垃圾回收机制来说:是否回收一个对象的标准在于:是否还有引用变量引用该对象?只要有引用变量引用该对象,垃圾回收机制就不会回收它。
当Java对象被创建出来之后,垃圾回收机制会实时地监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。当垃圾回收机制实时地监控到某个对象不再被引用变量所引用的时候,立即回收机制就会回收它所占用的空间。
可以把jvm内存中对象引用理解成一种有向图,把引用变量、对象都当成为有向图的顶点,将引用关系当成图的有向边,有向边总是从引用端指向被引用的Java对象。因为Java所有对象都是由一条一条线程创建出来的,因此可把线程对象当成有向图的起始顶点。
对于单线程程序而言,整个程序只有一条main线程,该图就是以main进程为顶点的有向图。在这个有向图中,main顶点可达的对象都处于可达状态,垃圾回收机制不会回收它们;如果某个对象在这个有向图中处于不可达状态,那么就认为这个对象不再被引用,接下来垃圾回收机制就会主动回收它了。
JVM的垃圾回收机制采用有向图方式来管理内存中的对象,因此可以方便地循环引用的问题。采用有向图来管理内存中的对象具有高的精度,但缺点是效率较低。
当一个对象在堆内存中运行的时候,根据它在对应有向图中的状态,可以把它所处的状态分成如下3种:
可达状态:当一个对象被创建后,有一个以上的引用变量引用它。在有向图中可从起始顶点导航带该对象。那么它就处于可达状态,程序可通过引用变量来调用该对象的属性和方法。
可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将先进入可恢复状态,此时从有向图的起始顶点不能导航到该对象。在这个状态下,系统的垃圾回收机制准备回收该对象所占用的内存。在回收该对象之前,系统会调用可恢复状态的对象的finallize方法进行资源清理,如果系统在调用finalize方法重新让一个以上引用变量引用该对象,则这个对象会再次变为可达状态;否则,该对象将进入不可达状态。
不可达状态:当对象的所有关联都被切断,且系统调用所有对象的finalize方法依然没有使该对象变成可达状态,那这个对象将永久性地失去引用,最后变成不可达状态。只有当一个对象处于不可达状态时,系统才会真正回收该对象所占有的资源。
一个对象可以被一个方法局部变量所引用,也可以被其他类的类变量引用,或者被其他对象的实例变量性引用。当某个对象被其他类的类变量引用时,只有该类被销毁后,该对象才会进入可恢复状态;当某个对象被其他对象的实例变量引用时,只有当引用该对象的对象被销毁或变成不可达状态后,该对象才会进入不可达状态
对于垃圾回收机制来说,判断一个对象是否可回收的标准在于该对象是否被引用。
从JDK1.2开始,Java在Java.lang.ref包下提供了3个类;SoftReference、PhantomReference和WeakReference.
它们分别代表了系统对象的3种引用方式:软引用、虚引用和弱引用。归纳起来,Java语言对对象的引用有如下4种:
1:强引用(程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。Java程序可以通过强引用来访问实际的对象。当一个对象被一个或一个以上的强引用变量所引用时,它处于可达状态,它不能被系统垃圾回收机制回收。强引用是Java编程中广泛使用的引用类型,被强引用所引用的Java对象绝不会被垃圾回收机制回收,即使系统内存非常紧张;即使有些对象以后永远都不会被用到,JVM也不会回收被强引用所引用的Java对象。由于jvm肯定不会回收强引用所引用的Java对象,因此强引用是造成Java内存泄漏的主要原因之一)
2:软引用(需要通过softReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象;当系统内存空间不足时,系统将会回收它。软引用通常用于对内存敏感的程序中,软引用时强引用很好的替代。对于被强引用所引用的Java对象而言,无论系统的内存如何紧张,即使某些Java对象以后再也不可能使用,垃圾回收机制依然不会回收它所占用的内存。对于软引用则不同,当系统内存空间充足时,软引用与强引用没有太大的区别;当系统内存空间不足时,被软引用所引用的Java对象可以被垃圾回收机制回收,从而避免系统内存不足的异常。当程序大量的创建某个类的新对象,而且有可能重新访问已创建老对象时可以充分使用软引用来解决内存紧张的难题。)
3:弱引用(弱引用与软引用有点相似,区别在于弱引用所引用对象的生存期更短。弱引用通过weakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当一个对象只有弱引用时,必须等到系统垃圾回收机制运行时才会被回收。弱引用具有很大的不确定性,因为每次垃圾回收机制执行时都会回收弱引用所引用的对象,而垃圾回收机制的运行又不受程序员的控制,因此程序获取弱引用的Java对象时必须小心空指针异常—通过弱引用所获取的Java对象可能是null.当程序有大量的Java对象需要使用弱引用来引用时,可以考虑使用weakHashMap来保存它们。在垃圾回收之前,weakhashmap的功能与普通hashmap并没有太大的区别,功能完成相似。一旦垃圾回收机制被执行,weakhashmap中所有的key-value都会被清空,除非某些key还有强引用在引用它们
)
4:虚引用(跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含指定的虚引用,从而了解虚引用所引用对象是否即将被回收。引用队列由Java.lang.ref.ReferenQueue类表示,用于保存被回收后对象的引用。当把软引用和弱引用和引用队列联合使用时,系统回收被引用对象之后,将会把被回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到它的关联的引用队列中,这使得可以在对象被回收之前采取行动。虚引用通过PhantoRreference类实现,它完全类似于没有引用。引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用,那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列联合使用)。
使用这些引用类可以避免在程序执行期间将对象留在内存中,如果以软引用、弱引用或虚引用的方式引用对象、垃圾回收机制就能够随意地释放对象。如果希望尽可能减小程序在其生命周期中所占用的内存大小
,这些引用类就很有好处。
要使用这些特殊的引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,就会浪费这些类所提供的任何好处。