一、怎样判断对象是否已经死亡
(1)、引用计数法
对象头里面有一个计数,当有一个栈中的变量指向这个对象,计数就加1。
当栈中的变量销毁,不再指向这个对象,计数就减1。当对象头里面的计数为0时,就标记为
可回收对象。
这样的方法m不能解决循环引用的问题,如果一个对象A持有对象B,
而对象B也持有一个对象A,A与B的counter恒大于1,会使得GC永远无法回收这两个对象。
(2)、可达性分析法
从堆中的对象向上查找,一直到GC root(栈变量\静态变量、静态常量等方法区或栈中的数据)
没有任何关系链连接的对象,也就是找不到GC root的对象,就是可回收的对象。
二、垃圾收集算法
1、标记清除法
经过可达性分析法,将可以回收的对象标记出来,直接清除掉。
这种方式会造成大量的空间碎片,因为这些可回收对象不是连续的。
2、复制算法
将内存分为两份,先将还有效的对象复制到另外一份内存里面去,然后将剩下的全部清除。
这样就解决的空间碎片的问题,但是会浪费一半的内存空间。
适用于有效对象比较少的场景,因为要复制的对象比较少,效率比较高,比如新生代的GC就是用的这种算法。
3、标记整理法
在标记清除法的基础上,将有效的对象重新进行整理,整理到一起,解决空间碎片的问题。
适用于对象生存周期长,对象多的场景,解决了空间碎片的问题,又不会浪费一半的内存空间,
节约内存,老年代的gc算法就是这样的。但是整理碎片效率会比较慢,
所以不适用于频繁gc的情况。
三、GC流程
一般是分代清除,将内存分为新生代和老年代,新生代分为(eden、fromSurvivous和to Survivous)。
1、在不断创建对象的过程中,当Eden区域被占满,此时会开始做Young GC也叫Minor GC
Minor GC:
<1>、eden区有效对象转移到to Survivous中,to Survivous装不下的对象转移到老年代区。
<2>、清理eden区的无效对象。
<3>、下一次eden区满了之后,将eden区和to Survivous区有效的对象转移到fromSurvivous区,
fromSurvivous区放不下的数据转移到老年代区,
然后清理eden区和to Survivous区无效对象。
<4>、下一轮又是将fromSurvivous和eden区有效数据放入to Survivous区中,
这样来回倒,15次之后还存在于新生代的对象,就直接转入老年代区。
<5>、老年代区满了之后,整个系统会暂停运行,进行full gc,统一清理新生代和老年代的对象。
full gc是很影响程序效率的,jvm调优就是为了减少full gc的频率。
三、如何进行gc调优
(1)、GC调优的目的:
一个是将转移到老年代的对象数量降到最少
另一个是减少Full GC的执行时间
jvm管理的内存调大,减少full gc频率。
(2)、将转移到老年代的对象数量降到最少:
ygc每次晋升到old gen的内容较多,而这很可能是因为JVM 动态的调整eden 和 survivor区,
导致它们空间过小,部分本该在new gen呆着的对象直接跳到old gen
了(此现象在survivor区较为明显,因为其本来就较小)
可以固定好eden 和 survivor区的大小,如果是程序需要大量产生临时对象,
可以将新生代内存调大一点。
(3)、减少Full GC的执行时间
使用CMS GC,启用碎片整理,降低fgc的耗时。
(4)、选中合适的垃圾回收器
新生代的垃圾回收器都是使用的复制算法(S0区和S1区)
老年代的垃圾收集器使用的算法是标记-压缩和标记清除算法,
只有CMS使用的是标记-清除算法,
其他的老年代收集器都是使用的标记-压缩算法。
G1既回收新生代又回收老年代,所以使用的是复制算法和标记-压缩算法。