GC是在什么时候,对什么东西,做了什么事情?”
GC:系统自身决定,不可预测的时间/调用System.gc()的时候。2.超出作用域的对象/引用计数为空的对象。3.
gc不仅负责垃圾回收,还决定内存分配。从gc root开始搜索,搜索不到的对象。
java内存管理主要是对内存中的对象进行内存的分配和回收,我们都知道当我们创建一个对象时,对象的引用放在栈(Stack)中,对象放在堆(heap)中,gc只回收堆里面的对象。当gc检测到一个堆中的一个对象不在被引用时,就会对这个对象进行回收。
当我们创建一个对象时,gc就会监视这个对象的地址,大小以及状态。gc有特定的回收算法,通常使用有向图来记录管理堆中的对象,通过这种方式来确定那些对象正在被引用,那些已经不在被引用,当一个对象不在被引用时,gc就有权回收这个对象。当然可以使用System.gc();Runtime.getRuntime().gc();来显示调用gc。但是java规范不保证gc不一定立即回收。
gc可以使我们在开发时候不用考虑内存回收的事情了,可以防止内存泄漏。
垃圾收集的实现种类:
1. 引用计数:
一个A对象,如果有程序使用了这个引用对象,那么引用计数加1,当一个对象使用完毕之后引用计数减1,那么引用计数为0的时候,则可以回收。
他不能识别循环引用
2. 跟踪收集:
root set :当前正在执行的线程;全局或者静态变量;JVM Handles;JNDI Handles
从root set 开始扫描有引用的对象,如果某个对象不可以到达,则说明这个对象已经dead,GC可以对其回收。也就是说:如果A对象引用了B对象,那么虚拟机会记住这个引用路径,同时B对象引用了C对象,那么也会记录这个路径如果一个对象没有在路径图中,那么可以收集。
需要维护一张引用的全景图,增加了内存开销,和图的遍历开销。
3. 基于对象跟踪的分代增量收集 :
基于对象跟踪:说明是由跟踪收集发展而来的,分代是指对堆进行了合理的划分,
增量收集:不是每一次全部收集,而是累积的增量收集。
Sun的JVM将整个堆分为三代:YoungGen(新生代),OldGen(年老代),和PermGen(持久区):
Minor GC:通常是指对新生代的回收。
Major GC:通常是指对年老代的回收。
Full GC:Major GC除并发gc外均需对整个堆进行扫描和回收。
复制拷贝算法:要拷贝大量数据,不会产生碎片。
标记算法:从引用根节点开始标记所有被引用的对象,把未被引用的对象清除。要遍历所有对象,会产生碎片。
young 又分为eden,survivor1(from space ),survivor2(to sapce ).youngGen区里面的对象的生命周期比较短,gc对这些对象进行回收的时候采用复制拷贝算法。
eden 每当一个对象创建的时候分配的这个区域。当eden无法分配时,触发一次Minor gc。gc每次回收的时候都将eden区存活的对象和survivor1中的对象拷贝到survivor2中,eden和survivor1清空;当gc执行下次回收的时候将eden和survivor2中的对象拷贝到surivor1中,清空eden和survivor2。依次这样执行;经过数次回收将依然存活的对象复制到OldGen区。
OldGen 当对象从年轻代晋升到老年代之前,会检测老年区的剩余空间是否大于要晋升对象的大小,如果小于则直接进行一次Full GC,以便让老年去腾出更多的空间,然后再进行Minor GC,把年轻代的对象复制到老年代;如果大于,则根据条件(HandlePromotionFailure设置)进行Minor GC 和 Full GC。
老年区采用标记算法,因为老年区对象的生命周期都是比较长的,采用拷贝算法要拷贝大量的数据。采用标记算法每次gc回收都要遍历所有的对象。
PermGen 主要存放加载进来的类信息,包括方法,属性,常量池等,满了之后可能会引起out of memory 错误。