JVM系列(三)垃圾回收

什么对象需要被回收??

  没有被引用的对象要被回收。
 
怎么判定对象已经没有被引用???
1、引用计数算法。(因为循环引用问题,java没有使用这种方法)
2、可达性分析法。(主流实现。判定对象是否被引用。从GC ROOTS节点找引用链。)
 

gc roots是?

  • Class - class loaded by system class loader. Such classes can never be unloaded. They can hold objects via static fields. Please note that classes loaded by custom class loaders are not roots, unless corresponding instances ofjava.lang.Class happen to be roots of other kind(s).
  • Thread - live thread
  • Stack Local - local variable or parameter of Java method
  • JNI Local - local variable or parameter of JNI method
  • JNI Global - global JNI reference
  • Monitor Used - objects used as a monitor for synchronization
  • Held by JVM - objects held from garbage collection by JVM for its purposes. Actually the list of such objects depends on JVM implementation. Possible known cases are: the system class loader, a few important exception classes which the JVM knows about, a few pre-allocated objects for exception handling, and custom class loaders when they are in the process of loading classes. Unfortunately, JVM provides absolutely no additional detail for such objects. Thus it is up to the analyst to decide to which case a certain "Held by JVM" belongs.
 

对象引用是?

强引用

    如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足,JVM宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会考随意回收具有强引用的对象来解决内存不足的问题。

 

软引用

    如果一个对象具有软引用。如果内存空间足够。垃圾回收器不会回收它。如果内存不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

    软引用可用来实现内存敏感的高速缓存。

 

弱引用

    如果一个对象具有弱引用。当垃圾回收器发现只具有弱引用对象,不管当前内存空间足够与否,都会回收它的内存。

不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现只具有弱引用的对象。

 

虚引用

    虚引用不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

 

根据区域划分垃圾回收

新生代的垃圾回收(minor gc)
当新生代满了,执行的垃圾回收就是minor gc。
  • 绝大部分新创建的对象在eden区。
  • 当eden区满了, Minor GC 就执行,而且所有的survivor区域里面的对象,丢到一个survivor里面。
  • 在一个时间中,必定其中一个survivor区是空的。
  • 好多轮minor GC后还是存在的对象,会被移到老生代。一般是有一个门限(threshold for the age of the young generation )
 
老生代的垃圾回收(major gc)
老生代满了就执行这个gc
 
perm只在full gc下执行垃圾回收(目前发现只有cms会回收p区?)
 
执行上述的垃圾回收,都会暂停jvm里面的所有应用!
minor gc是很快的,但是major gc很耗时。
 
 
full gc就是分别执行minor gc,major gc,还垃圾回收了p区。
 
 

垃圾回收算法

1、标记--清除算法(mark-sweep)
最基本、最早的垃圾回收算法。一般是不适用的。
比如说某个generation内存耗尽时触发。
步骤如下:
--stop the world。
--遍历所有gc roots。可达到的表示为存活。
--遍历heap中的对象,将没有标记为存活的清除。
 
缺点:慢,需要遍历;清理出来的空间不是连续的。
(参考:http://www.th7.cn/Program/java/201308/146907.shtml)
 
2、复制(copying)
常用的实现算法。
把一个区域中存活的复制到另外一个区域中。
比如说eden+一个survivor——>另外一个survivor。
也满了的话,丢到老生代。
 
缺点:对象存活率较高,就会变慢。
 
3、标记--整理(mark-compact)
老年代的实现方式。
--stop the world。
--遍历所有gc roots。可达到的表示为存活。
--所有存活的对象向一边移。
 
4、分代收集(generational collection)
当前的商用收集方法。
是2、3的综合体。根据对象特征, 使用2还是使用3。

垃圾收集

无论哪一种垃圾收集器, 都会stop the world。只是停顿时间长短区别。
不同的垃圾收集器有不同的作用。
 
serial收集器:
最早的实现。
单线程。
用于新生代。
使用复制算法。
还在使用,运行在client模式下的虚拟机是不错的。
 
Parallel Scavenge:
多线程,用于新生代。
使用复制算法。
可以用于server模式。
不建议单CPU下使用。
 
ParNew收集器:
增强版Parallel Scavenge,可以与CMS配合使用。
 
Serial old:
单线程。
标记--清除算法。
用于老生代
 
Parallel Old:
多线程
标记-整理算法。
用于老生代
 
CMS:
多线程, 用于老生代。
两次stop the world。
cms一般是sweep(清除)操作,而不是。整理后空间是不连续的。
有一个阀值,空间不连续到一定程度后compact(整理)。或者进行full gc
有一个参数设置,可以收集p区。

  • 初始标记(init mark):收集根引用,这是一个stop-the-world阶段。
  • 并发标记(concurrent mark):这个阶段可以和用户应用并发进行。遍历老年代的对象图,标记出活着的对象。
  • 并发预清理(concurrent preclean):这同样是一个并发的阶段。主要的用途也是用来标记,用来标记那些在前面标记之后,发生变化的引用。主要是为了缩短remark阶段的stop-the-world的时间。
  • 重新标记(remark):这是一个stop-the-world的操作。暂停各个应用,统计那些在发生变化的标记。
  • 并发清理(concurrent sweep):并发扫描整个老年代,回收一些在对象图中不可达对象所占用的空间。
  • 并发重置(concurrent reset):重置某些数据结果,以备下一个回收周期开始。
 
G1:
不区分新生代还是老生代的垃圾收集器。基于标记--整理。没有碎片。
 
  不同的垃圾回收器,作用在何处:
 
 
参考:
 
 
有道云笔记:http://note.youdao.com/share/?id=4e102baa9a3fa30230ff3837f6a9e79e&type=note
原文地址:https://www.cnblogs.com/ELMND/p/4631166.html