当对象被创建时,就会在Java虚拟机的堆区中拥有一块内存,在Java虚拟机的生命周期中, Java程序会陆续地创建无数个对象,假如所有的对象都永久占有内存,那么内存有可能很快被消耗光,最后引发内存空间不足的错误。
因此必须采取一种措施来及时回收那些无用对象的内存,以保证内存可以被重复利用。
Java GC (Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专编写内存回收和垃圾清理代码,对内存泄露和溢出的问题。
自动垃圾回收是查看堆内存、识别哪些对象正在使用、哪些未使用以及删除未使用对象的过程。一个使用中的对象,或一个引用的对象,意味着你的程序的某些部分仍然维护着一个指向那个对象的指针。未使用的对象或未引用的对象不再被程序的任何部分引用。因此可以回收未被引用的对象使用的内存。
在像C这样的编程语言中,分配和释放内存是一个手动过程。 在Java中,释放内存的过程由垃圾收集器自动处理
什么是gc垃圾回收机制?
GC垃圾回收机制是java特有的,由垃圾收集器自动处理。
GC垃圾回收机制是查看堆内存、识别哪些对象正在使用、哪些未使用以及删除未使用对象的过程。用来清理无用对象占用的内存,提高内存使用效率。
gc垃圾回收机制主要发生在哪些内存区域?为什么?
gc垃圾回收机制主要发生在java虚拟机的java堆和方法区中。(在程序运行期间,这部分内存的分配和使用都是动态的。)因为java堆和方法区是各线程共享的,且java对象主要存放在堆中。
而jvm的程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,实现了自动的内存清理。
需要GC的内存区域?线程生命周期?线程死亡?
jvm中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于java堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。
判断一个对象是否存活常用的有两种办法:引用计数和可达分析:
引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
Java没有选择使用引用计数算法管理内存。
从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。
在Java语言中,GC Roots包括: .
●虚拟机栈中引用的对象。
●方法区中类静态属性实体引用的对象。
●方法区中常量引用的对象。
●本地方法栈中JNI引用的对象。
要真正宣告一个对象的死亡,至少要经历两次的标记过程:
第一次标记在可达性分析后发现到GC Roots没有任何引用链相连时,被第一次标记。并且判断此对象是否必要执行finalize ()方法!
如果对象没有覆盖finalize()方法或者finalize()已经被JVM调用过,则这个对象就会认为是垃圾,可以回收。对于覆盖了finalize()方法, 且finalize()方法没有被JVM调用过时,对象会被放入一个成为F-Queue的队列中,等待着被触发调用对象的finalize() 方法。
第二次标记finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法
执行完第一次的标记后, GC将对F-Queue队列中的对象进行第二次小规模标记。也就是执行对象的finalize()方法!
如果对象在其finalize()方法中重新与引用链上任何一个对象建立关联,第二次标记时会将其移出"即将回收"的集合。如果对象没有,也可以认为对象已死,可以回收了。 什么时候触发GC?
程序调用System. gc() 时可以触发系统自身来决定GC触发的时机(根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程) GC分类
GC又分为Minor GC和Full GC(也称为Major GC)
Minor GC触发条件Minor GC的触发条件是,当Eden区满时
GC常用算法GC收集过程用到的算法有:
标记清除算法标记-压缩算法复制算法分代收集算法 标记-清除算法 Step 1:标记(Marking):
GC通过遍历内存区辨别哪些内存在使用,哪些内容没有使用。并做好标记。
引用的对象以蓝色显示。未引用的对象以金色显示。在标记阶段扫描所有对象以进行此确认。如果必须扫描系统中所有的对象,这可能是一个非常耗时的过程。 Step 2:清除(Normal Deletion):
清除阶段移除掉垃圾对象,并且用一个链表维护空闲的区域。
内存分配器持有空闲内存区的引用,以便于分配内存给新对象。 优缺点 优点
每个活着的对象的引用只需找到即可,找到一个就可以判断它为活。
不移动对象的空间
缺点效率较低(递归与全对象遍历),每个活着的对象都要在标记阶段遍历一遍;所有的对象都要在清除阶段扫描一遍,因此算法复杂度比较高;没有移动对象,可能导致很多碎片空间无法利用的情况 标记-压缩算法 分代收集算法



