1:对象的回收前期工作
finalize()方法的调用:
当一个对象不再使用时会被java虚拟机销毁,并回收其所占内存。java中并没有c++中类似析构函数这样的方法,当调用对象的delete()方法时,在析构函数中做一些资源释放的工作。
1)在java中,当我们调用System.gc()时,Java垃圾回收并不会立即执行或者根本不会执行,这主要取决于java虚拟机此时对程序运行状态的一个评测。
2)当一个对象被垃圾回收器回收前,会首先会调用其finalize()方法,在这个方法中我们可以做一些与内存无关的内存回收工作,例如对文件的访问、socket的连接断开、jni资源的释放,最后还要调用super.finalize()。
2:垃圾回收器的工作方式:“标记-清扫”和“停止-复制”
1)java中所有对象都是在堆内存中创建的
2)堆内存的分配,由于java垃圾回收器的介入,使得对象的内存分配效率提高很多
标记-清扫
在进行一次gc的过程中,java虚拟机会扫描堆内存和静态存储区的所有对象,并对其进行“存活”状态标记,扫描完成后(扫描过程中不清理对象,扫描完成后才开始清理),将没有被标记为“存活”状态的对象进行销毁(不会发生复制“存活”对象到另一个堆区域的动作),也就是所谓GC ROOT无法到达的对象,此时会存在很多不连续的堆空间,如果垃圾回收器希望得到连续的空间,就需要重新整理“存活对象”。此过程进行前,程序需要暂停。
停止-复制
垃圾清理过程中,死对象清理的同时,会将“活对象”复制到另一个堆中,以使所有“活对象”拥有连续的内存空间,这样为新对象的内存分配提高了效率,但也带来了问题。第一需要两个堆内存区域,“活对象”需要在这两个区域进行复制,这样内存开销太大;第二由于对象指向的内存发生了变化,那么此时就需要对对象进行地址修改。此过程进行前,程序需要暂停。
随着java虚拟机的新技术出现,对“死对象”和“活对象”内存有了改善。此时虚拟机会按块分配内存,当一个对象被销毁后,此块就会被标记为废弃,此时就可以直接往废弃块中拷贝对象,这样就不必分配两个单独的堆区域,同时垃圾回收器也会在回收之后对内存进行整理。
总结
针对以上“标记-清扫”和“停止-复制”,java虚拟机采用一种被称为“自适应”的方式工作。当内存充足,gc频率低,程序处于一种比较稳定的状态时,此时就会采用“标记-清扫”模式。但是在长期的gc之后,如果碎片化比较严重(因为“标记-清理”模式,只是释放"死对象"堆内存,并未对区域进行整理),此时就会切换到“停止-复制”模式。