大白之前去面试,竟然倒在了一对垃圾对象上,事情是酱紫的:面试官问大白有关垃圾回收的机制,大白都勉强应付了,后来面试官问大白:假如对象A引用了对象B,对象B又引用了对象A,JVM如何进行垃圾回收?大白脑海里只出现了一副纠结的画面:
大白纠结了一小会,没有答出来,自恃熟悉垃圾回收的大白就这样小阴沟里翻了船。
判断对象是否存活
如何判断一个对象是否已经GG了,用腿都可以想到的办法,引用计数,引用计数算法(reference counting)见名知义,对象被引用一次就increase一次,引用释放时,就decrease一次。大白就是把自己绕进去了,孤立互相引用的两个对象可以称之为一对垃圾。Java是通过可达性分析来判断对象是是否可被回收,万物皆有根,就像一棵树,只有从根到节点存在路径时,节点才是生效的(有点扯远了)。
(本段援引自深入理解Java虚拟机,见上图)
可达性算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(reference chain),当一个对象到gc roots不存在路劲时,则标示此对象不可达,可以被回收了。
垃圾回收的本质
为什么会有垃圾回收,说白了就是因为内存不够用了,如果单机内存无限大,根本不需要回收了,随便用。无限大的单机内存,memcache,redis统统可以扔掉了,还有什么网络内存会比本机内存更快。又扯远了,简单测试一下垃圾回收,jvm-args,jdk1.8.0_144:
-XX:+PrintGCDetails -Xms32m -Xmn8m -Xmx32m
gc日志如下:
可以看到由于Allocation Failure内存分配失败导致了gc,PSYoungGen是使用Parallel Scavenge收集器回收年轻代,ParOldGen是使用
Parallel Old收集器回收老年代,其它的回收器还有CMS(Concurrent Mark Sweep),G1 (Garbage-First)。
回收器所采用的算法
常见的垃圾回收算法有①标记-清除算法,标记回收的对象,然后清除标记对象,比较简单高效,缺点也很明显,造成了很多空间碎片。②复制算法,最直观的例子就是eden区存活下来的对象移到survivor1区,再次执行minor gc时survior1区的对象复制转移到survior2区,survior1区全部清空。③标记-整理,可以看作是标记-清除结合复制算法。④分代收集算法,可以说不算是一种算法,只是不同代采用不同的回收算法。
常用分析工具
java自带了很多实用的工具,比如:jps -v,可以查看当前系统下java进程,对于从事java的童鞋不需要ps -ef | grep java。又扯远了,可以使用jmap生成堆转储快照,可以使用jvisualvm.exe工具直观查看哪些垃圾实例对象过多导致内存溢出。jstack工具可以生成线程快照,可以直观查看哪些线程挂起,或是等待什么资源,导致线程没有响应。
jstack -l pid > stack.log
jmap -dump:live,format=b,file=dump.dump pid