引用计数算法(Reference Counting)
给对象添加一个引用计数器, 每当有一个地方引用它时, 计数器就加1。
当引用失效时, 计数器值就减 1。
任何计数器为0的对象是不可能再被使用的。
应用领域:
Java 虚拟机中没有选用 引用计数法来管理内存的原因:
很难解决对象之间相互循环引用的问题。
代码
package com.ronnie.gc;
public class ReferenceCounting {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
// 占点内存, 方便查阅GC日志
private byte[] bigSize = new byte[2 * _1MB];
public static void testGC(){
ReferenceCounting objA = new ReferenceCounting();
ReferenceCounting objB = new ReferenceCounting();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
// 假设发生 GC, objA 和 objB 是否能被回收
System.gc();
}
public static void main(String[] args) {
testGC();
}
}
执行后打印的GC日志:
[GC (System.gc()) [PSYoungGen: 7999K->872K(75776K)] 7999K->880K(249344K), 0.0013158 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 872K->0K(75776K)] [ParOldGen: 8K->655K(173568K)] 880K->655K(249344K), [Metaspace: 3440K->3440K(1056768K)], 0.0054783 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 75776K, used 650K [0x000000076b780000, 0x0000000770c00000, 0x00000007c0000000)
eden space 65024K, 1% used [0x000000076b780000,0x000000076b822a68,0x000000076f700000)
from space 10752K, 0% used [0x000000076f700000,0x000000076f700000,0x0000000770180000)
to space 10752K, 0% used [0x0000000770180000,0x0000000770180000,0x0000000770c00000)
ParOldGen total 173568K, used 655K [0x00000006c2600000, 0x00000006ccf80000, 0x000000076b780000)
object space 173568K, 0% used [0x00000006c2600000,0x00000006c26a3d38,0x00000006ccf80000)
Metaspace used 3447K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 376K, capacity 388K, committed 512K, reserved 1048576K
Process finished with exit code 0
可达性分析算法
主流的商用编程语言 (Java, C#, 以及古老的Lisp) 的对象是否存活判断的算法实现都是 可达性分析(Reachability Analysis)。
基本思想
通过一系列的 GC Root 对象作为起始点, 从这些节点开始向下搜索, 搜索走过的路径称为引用链(Reference Chain)
当一个对象 到 GC Roots 没有任何引用链相连(GC Roots 到这个对象不可达), 则证明此对象是不可用的。
拓展的: 新生代GC搜索用深度优先算法, 老年代GC搜索用广度优先算法(忘了的去补图论)。
Java 中可以作为 GC Roots 的对象:
引用
无论是通过引用计数法来判断对象的引用数量, 还是通过 可达性分析算法判断对象的引用链是否可达, 判断对象是否存活都与 “引用” 有关。
JDK 1.2 之前 Java中引用的定义:
JDK 1.2之后, Java对引用的概念进行了扩充, 将引用分为强引用(Strong reference), 软引用(Soft Reference), 弱引用(Weak Reference), 虚引用(Phantom Reference) 4 种, 强度依次减弱。
强引用(Strong reference)
软引用(Soft Reference)
描述一些还有用但并非必须的对象。
对于软引用关联着的对象, 在系统将要发生内存溢出异常之前, 将会把这些对象列进回收范围之中进行第二次回收。
如果这次回收还没有足够的内存, 才会抛出内存溢出异常。
在 JDK 1.2 之后, 提供了SoftReference 类来实现软引用。
package java.lang.ref;
/**
* Soft reference objects, which are cleared at the discretion of the garbage
* collector in response to memory demand.
* 软引用对象, 会在垃圾回收时 根据 内存需求进行清理。
* Soft references are most often used to implement memory-sensitive caches.
* 软引用最多被应用的场景是 处理内存敏感的缓存
*
* <p> Suppose that the garbage collector determines at a certain point in
* time that an object is <a href="package-summary.html#reachability">softly
* reachable</a>.
* 假设垃圾回收器认定某个时间一个时间的特定点的一个对象是软可获取的。
*
* At that time it may choose to clear atomically all soft
* references to that object and all soft references to any other
* softly-reachable objects from which that object is reachable through a
* chain of strong references.
* 在那个时间, 它可能会选择去原子性地清除所有指向该对象的软引用, 并且所有指向其他软可
* 获取的对象的 软引用都可以通过强引用连接获取。
*
* At the same time or at some later time it will enqueue those newly-cleared
* soft references that are registered with reference queues.
* 同时或者 稍后, 它会将这些已经注册到引用队列中的 新清理的软引用 放入队列
*
* <p> All soft references to softly-reachable objects are guaranteed to have
* been cleared before the virtual machine throws an
* <code>OutOfMemoryError</code>.
* 在虚拟机抛出 OOM 异常之前, 所有指向软可获取的对象的软引用 都 一定会被清理完。
*
* Otherwise no constraints are placed upon the time at which a soft reference
* will be cleared or the order in which a set of such references to different
* objects will be cleared.
* 否则 当一个软链接被清除时 或者 引用指向不同对象的引用集合被清除的顺序 清除时, 就不
* 会有任何限制。
*
* Virtual machine implementations are, however, encouraged to bias against
* clearing recently-created or recently-used soft references.
* 然而虚拟机 的应用 更偏向于 不清理 最近创建的 和 最近被使用的软引用
*
* <p> Direct instances of this class may be used to implement simple caches;
* this class or derived subclasses may also be used in larger data structures
* to implement more sophisticated caches.
* 该类的直接实例可能被用于实现简单的缓存, 此类 或者继承此类的子类 可能会被用于大的数
* 据结构来实现更成熟的缓存。
*
* As long as the referent of a soft reference is strongly reachable, that is,
* is actually in use, the soft reference will not be cleared.
* 只要一个软引用指向的对象是强可获取的, 并且在被使用中, 那么软引用就不会被清除。
*
* Thus a sophisticated cache can, for example, prevent its most recently used
* entries from being discarded by keeping strong referents to those entries,
* leaving the remaining entries to be discarded at the discretion of the
* garbage collector.
* 因此, 一个成熟的缓存可以(举例)通过对 最近最多使用的 引用entries 维持强引用 来 防
* 止它被 丢弃。剩下的 引用 entries 会在 垃圾收集器 分离时被丢弃。
*
* @author Mark Reinhold
* @since 1.2
*/
public class SoftReference<T> extends Reference<T> {
/**
* Timestamp clock, updated by the garbage collector
* 由垃圾回收器 更新的 时间戳时钟
*/
static private long clock;
/**
* Timestamp updated by each invocation of the get method.
* 每次调用 get 方法时 都会更新 时间戳
* The VM may use this field when selecting soft references to be cleared,
* but it is not required to do so.
* 当虚拟机可能使用该域时可能会清除软引用, 但这不是必须的的。
*/
private long timestamp;
/**
* Creates a new soft reference that refers to the given object.
* 创建指向该对象的新的软引用
* The new reference is not registered with any queue.
* 该引用没有被注册到任何队列中
* @param referent object the new soft reference will refer to
*/
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
/**
* Creates a new soft reference that refers to the given object and is
* registered with the given queue.
* 创建一个指向该对象的新的软引用, 且该引用被注册到了已得的队列中
*
* @param referent object the new soft reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*
*/
// ReferenceQueue 是一个通过 volatile 关键字修饰实现的引用队列
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
/**
* Returns this reference object's referent.
* 返回该引用的对象
* If this reference object has been cleared, either by the program or by
* the garbage collector, then this method returns <code>null</code>.
* 如果该引用对象已经被清除了(无论是程序或者垃圾回收器执行的), 该方法都会返回
* null
* @return The object to which this reference refers, or
* <code>null</code> if this reference object has been cleared
*/
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
}
弱引用(Weak Reference)
也是用于描述非必须对象的, 但是它的强度比软引用更弱一些, 被弱引用关联的对象只能生存到下一次GC发生之前。
当垃圾收集器工作时, 无论当前内存是否足够, 都会回收掉只被弱引用关联的对象。
JDK1.2 之后, 提供了 WeakReference 来实现软引用。
package java.lang.ref;
/**
* Weak reference objects, which do not prevent their referents from being
* made finalizable, finalized, and then reclaimed.
* 软引用对象, 并不能保护他们指向的对象 不被 finalizable, finalized 再重申
*
* Weak references are most often used to implement canonicalizing mappings.
* 软引用被应用于实现最简洁的 mappings
*
* <p> Suppose that the garbage collector determines at a certain point in
* time that an object is <a href="package-summary.html#reachability">weakly
* reachable</a>.
* 假设 垃圾回收器 确定了在一个特定的时间点 的一个对象是 弱可获取的。
*
* At that time it will atomically clear all weak references to
* that object and all weak references to any other weakly-reachable objects
* from which that object is reachable through a chain of strong and soft
* references.
* 这时, 它会原子性地清理所有指向该对象的弱引用 并且 所有指向其他可以通过强引用链和软
* 引用链获取的弱可获取的对象的 弱引用。
*
* At the same time it will declare all of the formerly weakly-reachable
* objects to be finalizable.
* 同时, 它会声明所有的正式的 弱可获取对象 为 finalizable。
*
* At the same time or at some later time it will enqueue those newly-cleared
* weak references that are registered with reference queues.
* 在同一时间或者 稍后, 它会将这些已经注册在 引用队列中的新被清理的软引用 放入队列中
*
* @author Mark Reinhold
* @since 1.2
*/
public class WeakReference<T> extends Reference<T> {
/**
* Creates a new weak reference that refers to the given object.
* 创建指向该对象的新的软引用
* The new reference is not registered with any queue.
* 该引用没有被注册到任何队列中
* @param referent object the new weak reference will refer to
*/
public WeakReference(T referent) {
super(referent);
}
/**
* Creates a new weak reference that refers to the given object and is
* registered with the given queue.
* 创建一个指向该对象的新的软引用, 且该引用被注册到了已得的队列中
*
* @param referent object the new weak reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
虚引用(Phantom Reference)
是最弱的一种引用关系。
一个对象是否有虚引用的存在, 完全不会对其生存时间构成影响。
也无法通过虚引用来取得一个对象实例。
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时找到一个系统通知。
JDK1.2 之后提供了 PhantomReference 类来实现虚引用。
package java.lang.ref;
/**
* Phantom reference objects, which are enqueued after the collector
* determines that their referents may otherwise be reclaimed.
* 虚引用对象, 在收集器确认 软引用对象指向的对象 可能不会被重申后将它们放入队列。
*
* Phantom references are most often used for scheduling pre-mortem cleanup
* actions in a more flexible way than is possible with the Java finalization
* mechanism.
* 虚引用 最多被应用于 将检尸 行为 安排得更灵活, 而不 仅是 使用 Java finalization
* 机制。
*
* <p> If the garbage collector determines at a certain point in time that the
* referent of a phantom reference is <a href="package-
* summary.html#reachability">phantom reachable</a>, then at that
* time or at some later time it will enqueue the reference.
* 假设 垃圾回收器 确定了在一个特定的时间点 的一个对象是 虚可获取的, 那么 在这个时间
* 或者稍后, 它就会进入引用队列。
*
* <p> In order to ensure that a reclaimable object remains so, the referent
* of a phantom reference may not be retrieved:
* 为了确保一个可重申的对象维持该状态, 一个虚引用执行的对象可能不会被检索
* The <code>get</code> method of a phantom reference always returns
* <code>null</code>.
* 虚引用的 get 方法 总是返回 null
*
* <p> Unlike soft and weak references, phantom references are not
* automatically cleared by the garbage collector as they are enqueued.
* 不像软引用和弱引用, 虚引用 并不是 由 垃圾收集器 在 它们进入队列时自动清理的。
*
* An object that is reachable via phantom references will remain so until all
* such references are cleared or themselves become unreachable.
* 一个 可以通过软引用获取的 对象会维持原样 直到 所有 引用都被清除, 或者 指向的对象自
* 己变为了不可获取状态。
*
* @author Mark Reinhold
* @since 1.2
*/
public class PhantomReference<T> extends Reference<T> {
/**
* Returns this reference object's referent.
* 返回该引用指向的对象
* Because the referent of a phantom reference is always inaccessible,
* this method always returns <code>null</code>.
* 因为仅虚引用指向的对象往往是不可获取的, 该方法总会返回 null
*
* @return <code>null</code>
*/
public T get() {
return null;
}
/**
* Creates a new phantom reference that refers to the given object and
* is registered with the given queue.
* 创建一个指向该对象的新的软引用, 且该引用被注册到了已得的队列中。
*
* <p> It is possible to create a phantom reference with a <tt>null</tt>
* queue, but such a reference is completely useless: Its <tt>get</tt>
* method will always return null and, since it does not have a queue, it
* will never be enqueued.
* 虽然创建虚引用时也可能会附带一个空队列, 但这毫无使用价值, 由于 get 方法一直是
* 返回 null, 既然它没有队列, 那么它就不会被放入队列中。
*
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
标记清除法(Mark-Sweep 算法)
是基础的收集算法, 后续的收集算法都是基于这种思路并对其不足进行改进而得到的。
标记阶段
清除阶段
不足之处
示意图
复制法(Copying)
大致思想
优点
缺点
将内存缩小为原来的一半, 代价过高。
在对象存活率较高时就要进行较多的复制操作, 效率将会变低。
如果不想要浪费 50% 的空间, 就需要更多的空间进行分配担保, 以应对被使用的内存中所有对象都 100% 存活的极端情况。因此, 老年代一般不能直接选用这种算法。
标记整理法(Mark-Compact)
标记过程
整理过程
示意图
分代收集法(Generational Collection)
是内存回收的具体实现
HotSpot虚拟机的垃圾回收机制
Serial 收集器
ParNew 收集器
Serial 收集器的多线程版本
是许多运行在Server模式下的虚拟机中首选的新生代收集器
除Serial收集器外, 只有它能与CMS收集器配合工作(jdk 1.7-1.8版本)
包含Serial收集器可用的所有控制参数:
收集算法、Stop The World、对象分配规则、回收策略都与Serial收集器完全一样。
在 CPU 核数非常多的环境下, 可以使用 --XX: ParallelGCThreads 参数来限制垃圾收集的线程数。
Parallel Scavenge 收集器
新生代收集器, 也是使用复制算法的多线程收集器。
特点:
提供了两个参数用于精准控制吞吐量
GC 自适应调节策略(GC Ergonomics)
-XX:+UseAdaptiveSizePolicy
开启后就不需要手工指定新生代的大小(-Xmn), Eden 与 Survivor 区的比例 (-XX:SurvivorRatio), 晋升老年代对象大小(-XX:PretenureSizeThreshold) 等细节参数了。
虚拟机会根据当前系统的运行情况收集性能监控信息, 动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。
Serial Old 收集器
Serial 收集器的老年代版本, 同样是一个单线程收集器。
使用 标记整理算法
主要被Client模式下的虚拟机使用。
在Server模式下的用途:
Parallel Old 收集器
Parallel Scavenge 收集器 的老年代版本
使用多线程和标记整理算法
JDK1.6之后才有该收集器, 解决了此前新生代 Parallel Scavenge 收集器一直处于比较尴尬的状态:
Parallel Scavenge + Parallel Old 应用场合:
CMS(Concurrent Mark Sweep) 收集器
是以获取最短回收停顿时间为目标的收集器。
应用
基于 “标记-清除” 算法实现, 运作过程
优点
缺点
对 CPU 资源非常敏感
无法处理浮动垃圾(Floating Garbage)
可能出现 “Concurrent Mode Failure” 失败而导致另一次Full GC 的产生。
浮动垃圾:
CMS 激活阈值:
参数: -XX:CMSInitiatingOccupancyFraction
JDK1.6: 92%
如果 CMS 运行期间预留的内存无法满足需要, 就会出现一次 “Concurrent Mode Failure” 失败, 这时虚拟机会启动后备预案:
收集结束时会有大量碎片产生, 提前触发 Full GC
调整参数:
-XX:+UseCMSCompactAtFullCollection(默认开启) , 用于在 CMS 收集器顶不住要进行 FullGC时开启内存碎片的合并整理过程, 代价是停顿时间变长。
-XX:CMSFullGCsBeforeCompaction, 用于设置执行多少次不压缩的 Full GC 后, 跟着来一次带压缩的 Full GC
G1(Garbage First) 收集器
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机运行在 Client 模式下的默认值, 打开次开关后, 使用Serial + Serial Old 的收集器组合进行内存回收 |
UseParNewGC | 使用 ParNew + Serial Old 的收集器组合进行内存回收 |
UseConcMarkSweepGC | 使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收, Serial Old 收集器作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用。 |
UseParallelGC | 虚拟机运行在Server模式下的默认值, 使用 Parallel Scavenge + Serial Old (PS MarkSweep) 的收集器组合进行内存回收。 |
UseParallelOldGC | 使用 Parallel Scavenge + Parallel Old 的收集器进行内存回收 |
SurvivorRatio | 新生代中 Eden 区域 与 Survivor 区域的容量 比值, 默认为8, 代表 Eden:Survivor = 8 : 1 |
PretenureSizeThreshold | 直接晋升到老年代的对象大小, 设置这个参数后, 大于这个参数的对象将直接在老年代分配 |
MaxTenuringThreshold | 晋升到老年代的对象年龄。每个对象在坚持过一次 Minor GC 之后, 年龄就增加1, 当超过这个参数值时就进入老年代 |
UseAdaptiveSizePolicy | 动态调整 Java 堆中各个区域的大小以及进入老年代的年龄 |
ParallelGCThreads | 设置并行 GC 时进行内存回收的线程数 |
GCTimeRatio | GC 时间占用总时间的比率, 默认值为99, 即允许 1% 的 GC 时间, 仅在使用 Parallel Scavenge 收集器时生效。 |
MaxGCPauseMillis | 设置 GC 的最大停顿时间。 仅在使用 Parallel Scavenge 收集器时生成。 |
CMSInitiatingOccupancyFraction | 设置 CMS 收集器 在老年代空间被使用多少后出发垃圾回收, 默认值为 68% |
UseCMSCompactAtFullCollection | 设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用 CMS 收集器时生效。 |
CMSFullGCsBeforeCompaction | 设置 CMS 收集器 在进行若干次垃圾回收后再启动一次内存碎片整理, 仅在使用 CMS 收集器时生效。 |
原文:https://www.cnblogs.com/ronnieyuan/p/11815756.html