GC:垃圾回收
GC英文全称为Garbage Collection,即垃圾回收。
Java中的GC就是对内存的GC。
Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。
Java对象的分配,程序员可以通过new关键字,Class的new-Instance方法等来显示的分配;而对象的释放,程序员不能实时的进行释放,这就需要GC来完成。
JVM常见的GC包括三种:Minor GC,Major GC与Full GC
针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:
部分收集(Partial GC):不是完整收集整个Java堆的垃圾收集,其中又分为:
整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集
注意:JVM在进行GC时,并非每次都对所有区域(新生代,老年代,方法区)一起回收的,大部分时候回收的都是指新生代
触发机制:
因为Java对象大多具备朝生夕死的特新,所以Minor GC非常频繁,一般回收速度也比较快.
Minor GC会引发STW,暂停其他用户线程,等垃圾回收结束,用户线程才恢复运行
指发生在老年代的GC,对象从老年代消失时,我们说"Major GC或Full GC"发生了、
一般出现Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)
触发机制:
PS:
Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长
如果Major GC后,内存还不足,就报OOM了
Major GC的速度一般会比Minor GC慢10倍以上
触发Full GC执行的情况有如下五种:
另外要特别注意: full GC是开发或调优中尽量要避免的
经研究,不同对象的生命周期不同,70%-99%的对象是临时对象
其实不分代完全可以,分代的唯一理由就是优化GC性能,如果没有分代,那所有的对象都在一个区域,当需要进行GC的时候就需要把所有的对象都进行遍历,GC的时候会暂停用户线程,那么这样的话,就非常消耗性能,然而大部分对象都是朝生夕死的,何不把活得久的朝生夕死的对象进行分代呢,这样的话,只需要对这些朝生夕死的对象进行回收就行了.总之,容易死的区域频繁回收,不容易死的区域减少回收.
当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(GenerationalCollection)[插图]的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:
这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
把分代收集理论具体放到现在的商用Java虚拟机里,设计者一般至少会把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域。顾名思义,在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。
分代收集并非只是简单划分一下内存区域那么容易,它至少存在一个明显的困难:对象不是孤立的,对象之间会存在跨代引用。
假如要现在进行一次只局限于新生代区域内的收集,但新生代中的对象是完全有可能被老年代所引用的,为了找出该区域中的存活对象,不得不在固定的GC Roots之外,再额外遍历整个老年代中所有对象来确保可达性分析结果的正确性,反过来也是一样。遍历整个老年代所有对象的方案虽然理论上可行,但无疑会为内存回收带来很大的性能负担。
为了解决这个问题,就需要对分代收集理论添加第三条经验法则:
依据这条假说,我们就不应再为了少量的跨代引用去扫描整个老年代,也不必浪费空间专门记录每一个对象是否存在及存在哪些跨代引用,只需在新生代上建立一个全局的数据结构(称为“记忆集”,RememberedSet),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。
所以说,虽然这种方法需要在对象改变引用关系(如将自己或者某个属性赋值)时维护记录数据的正确性,会增加一些运行时的开销,但比起收集时扫描整个老年代来说仍然是划算的。