众所周知,Java程序员写的代码是没有办法控制Java对象的内存释放的,完全有JVM暗箱操作.
虽然程序员把内存的释放的任务都交给了Java虚拟机,但是并不代表Java程序就不存在内存泄漏.
反而,某程度上,当出现内存泄漏,Debug会变得难度更大.
所以,Java程序员,有必要去了解Java虚拟机对于内存的管理以及垃圾回收的机制.
Java虚拟机是如何判断一个对象可以回收?
当一个对象没有被任何其他所引用时,这个对象被Java虚拟机视为可回收.
早起的虚拟机,使用引用计数的方法判断对象是否可回收.这个方法的好处是简单和效率高,却难以解决循环引用的问题.
现在主流的Java虚拟机都是使用可达性的分析对象是否可回收.从若干Root节点开始遍历,所有从Root节点能到达的对象,都是存活对象.
反之,其他对象就是可回收对象.可以想象,这是有若干个根节点的图,用图论术语就是判断每个节点的可达性.
强引用,软引用,弱引用,虚引用都是什么?
强引用Strong Reference就是平时最常见的,不加任何修饰的对象实例化,得到的都是强引用.
软引用Soft Reference,软引用的对象,当Java虚拟机垃圾回收时出现空间不足的情况,软引用的对象就会被回收.
弱引用Weak Reference,弱引用的对象,当Java虚拟机下一次垃圾回收时,无论如何都会被回收.
虚引用Phantom Reference,虚引用的对象任何一个时刻都不能取得其对象的实例.虚引用的使用在于跟踪对象的生死存亡,当虚引用被垃圾回收时,会收到一个系统通知.
垃圾回收算法都有哪些?
1.标记清除,顾名思义就是标记和清除两个阶段.这个算法的致命弱点是,会产生大量的内存碎片,导致后期即使有足够的内存空间也无法分配大的连续的内存空间.
2.复制算法,是把内存空间分成大小相等的两个区域,每次只使用其中一个区域.每次内存回收,都会把存活的对象复制到另外一个区域.
这个算法虽然解决了内存碎片的问题,但是是以内存缩小原来的一半为代价.
复制算法也常用在新生代的垃圾回收.因为大多数的对象都是朝生夕死的,不会存活过下一轮的垃圾回收.所以实际上并没有必要按照1:1的比例来划分内存空间.
现实情况,内存被划分为一个较大的eden区和两个较小的survivor区.HotSpot虚拟机的eden:survivor:survivor=8:1:1.
每次都会使用eden和其中一个survivor用来分配内存,当垃圾回收时会把存活的对象复制到另外一个survivor区.
当出现survivor的内存不足时,会动用老年代的内存空间.老年代起的是一个担保的作用.
3.标记整理算法.把存活的对象都标记出来,再移动到内存空间的一端,把端边界以外的内存空间都清除释放.老年代一般采用标记整理算法.
4.分代收集算法.一般把内存空间分成老年代和新生代,不同的区域根据对象存活的周期和特点,使用合适的垃圾回收算法.
新生代使用复制算法.老年代使用标记清除或者标记整理算法.