通过定义一系列称为 GC Roots 根对象作为起始节点集,从这些节点出发,穷举该集合引用到的全部对象填充到该集合中(live set)。
GC Roots 包括
虚拟机栈内引用对象
线程调用的方法使用的参数和局部变量等
方法区中的静态引用对象
Java虚拟机内部的引用
基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutOfMemoryError),系统类加载器。<br><br>
所有被同步锁synchronized持有的对象<br>
两大问题<br>
误报(本该被回收的对象,被标记为存活):已死亡对象被标记为存活,垃圾收集不到。多占用一会内存,影响较小。<br>
在标记的时候,由于需要标记的对象比较多,需要一定的时间,假设这个时候已经标记了一半了,还在标记剩余的一半,这个时候有一个线程将自己的一个引用置为null了,并且这个引用是在已经标记完成的那一半中的,这个时候就会出现误报——本该是垃圾被回收的,现在标记成了存活。
漏报:引用的对象(正在使用的)没有被标记为存活,被垃圾回收了。那么直接导致的就是JVM奔溃。(<font color="#b71c1c">STW</font>可以确保可达性分析法的准确性,避免漏报)<br>
其实就是标记已经结束了,这个时候其他线程创建了对象,但是这个对象的引用没有被标记,然后在垃圾回收的时候,这个对象就被回收掉了。
STW
Java虚拟机是利用可达性算法判断对象是否需要回收的,由于在GC进行时,必须暂停所有的Java执行线程(Sun称之为“Stop The World”),所以,虚拟机必须尽量的优化GC过程的效率,减少暂停的时间。<br>
GC 停顿会拖慢应用程序,在外界看来,它就像冻住了一样。在 GC 停顿期间发给服务器的请求会更晚收到响应,根据停顿时间的不同(传统的 GC 停顿有可能达到几十秒),客户端有可能会出现超时。如果客户端进行重试,服务器端就会有更多待处理的请求,这个时候需要使用断路器。<br>
长时间的 GC 停顿也可能造成服务的健康检测失效,并导致服务被重启。而在一个服务重启期间,其他服务需要承担更多的负载,它们所经历的停顿会更长,这就像是一个恶性循环。<br>
不可预测的 GC 停顿给系统带来的影响远远超过了应用程序本身。客户端出现回压,请求队列溢出,监控控制台满是各种超时异常,运维人员忙得团团转。对于一个可以应对各种情况的系统来说,需要在 CPU 时间、队列长度、可接受的响应时间方面具备缓冲能力。<br><br>