垃圾回收是什么
一种自动内存管理的一种形式,John McCarthy为了简化Lisp中的内存手动管理,于1959年发明了垃圾回收,其远比java历史永久
如何识别垃圾对象
引用计数算法 (Reference Counting)
计数机制
每当对象被引用,引用次数递增
引用消失或设置为null,引用次数递减
回收机制
对象引用次数为0时被回收
缺点
1.对象循环依赖时,引用次数不会变为0,永远不会被回收
示范代码:
public class ReferenceCountingGC {
public Object instance;
public static void main(String...strings) {
ReferenceCountingGC a = new ReferenceCountingGC(); // 1st instance
ReferenceCountingGC b = new ReferenceCountingGC(); // 2nd instance
a.instance = b;
b.instance = a;
a = null;
b = null;
}
}
2.对象需额外空间存储计数 & 额外的进程资源处理计数
可达性分析算法(Reachability Analysis)
基本思想
依赖引用链从"根"对象跟踪可达性对象,如果一个对象不能从任何GC roots可达,该对象会被视为垃圾对象("灰色对象")
GC roots类型
1.objects referenced in VM Stack;
2.objects referenced by static members or methods in Method Area;
3.objects referenced by constants in Method Area;
4.objects referenced by JNI in Native Method Stack.
怎么垃圾回收
Stop and Copy
1.Stop-and-Copy算法将内存一分为二,任何时候只有1份可用
2.回收垃圾的时候,会将所有'live objects'拷贝到另一份内存中,再把已使用过的内存空间一次清理掉
该算法的问题:
浪费"一份"内存
垃圾对象过多时,拷贝进程耗费资源
对象的应用都会被更改
Mark and Sweep
1.mark:跟踪所有应用以标记'live objects'、'dead objects'
2.sweep:清理'dead objects'
该算法问题:
内存碎片
Mark and Compact
1.mark:跟踪所有应用以标记'live objects'、'dead objects'
2.compact:将所有'live objects'移动到一端,可回收,未被引用的对象得以被区分开在另一端
该算法问题:
解决了内存碎片问题,但是效率也比其他两种算法低
java中垃圾回收
1.回收器只能回收以new方式创建的对象
2.提供finalize()方法来释放对象的特殊内存
内存管理与回收机制
java使用"Adaptive generational stop-and-copy mark-and-sweep collection"算法
堆内存被划分为几块:
1.Young Generation:所有新对象分配和老化的地方
- 年轻代又被分为eden、survivor space(由两块大小相同的s0、s1构成)
2.Old Generation(or Tenured Generation)
- 每次GC都会触发“Stop-The-World”。内存越大,STW 的时间也越长。
- 复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。
3. Permanent Generation:JVM 所需的元数据来描述应用程序中使用的类和方法
几个问题
为啥需要survivor space
充当缓冲角色,避免老年代过快填满,减少Major GC所带来的影响,Survivor的预筛选保证只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。
survivor space 为什么是两块
解决内存碎片问题:
- 若只有一块 & 此时该块中也有对象需要回收,这时只能采用标记-清除算法,该算法会导致内存碎片
- 当有两块时:每次Minor GC,会将eden、From 区中的存活对象复制到 To 区域,第二次Minor GC时,From 与 To 职责兑换,这时会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复
- 整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的
Major GC vs Full GC
- Major GC is cleaning the Tenured space.
- Full GC is cleaning the entire Heap – both Young and Tenured spaces.
java中何时触发gc
- Heap space allocation:JVM尝试为新对象分配内存,堆内存空间不足时
- System.gc() method call:
- Old generation threshold:CMS中设置JVM参数-XX:CMSInitiatingOccupancyFraction实现
- PermGen/Metaspace threshold:
- Time-based:
G1 Garbage Collector
特点
G1垃圾回收器计划是作为替换CMS长期计划,提供比CMS更多的可预测的垃圾收集暂停,允许用户指定暂停目标
堆结构
堆是1块内存区域,分成许多固定大小的区域
regions size由JVM启动时决定,大约2000个左右的regions,大小在1~32Mb不等
堆分配
这些块逻辑上被映射为 Eden, Survivor,和old区
这种区域的设计实现了可以在停止或不停止所有其他应用线程的情况下并行收集。
G1垃圾回收阶段
Young Generation Collection
YGC是一个会STW的阶段
活对象被疏散(即复制或移动)到一个或多个存活区域。如果达到了老化阈值,部分对象就会被提升到old区
计算下一个年轻 GC 的eden和survivor大小(暂停时间目标也会被考虑在内)
Old Generation Collection
阶段
Phase | Description |
(1) Initial Mark (Stop the World Event) | This is a stop the world event. With G1, it is piggybacked on a normal young GC. Mark survivor regions (root regions) which may have references to objects in old generation. |
(2) Root Region Scanning | Scan survivor regions for references into the old generation. This happens while the application continues to run. The phase must be completed before a young GC can occur. |
(3) Concurrent Marking | Find live objects over the entire heap. This happens while the application is running. This phase can be interrupted by young generation garbage collections. |
(4) Remark (Stop the World Event) | Completes the marking of live object in the heap. Uses an algorithm called snapshot-at-the-beginning (SATB) which is much faster than what was used in the CMS collector. |
(5) Cleanup (Stop the World Event and Concurrent) |
|
(*) Copying (Stop the World Event) | These are the stop the world pauses to evacuate or copy live objects to new unused regions. This can be done with young generation regions which are logged as [GC pause (young)] . Or both young and old generation regions which are logged as [GC Pause (mixed)] . |
G1注意事项
避免"Evacuation Failure"
当JVM 对存活对象和已晋升对象进行 GC 期间,堆区域耗尽,就会发生晋升失败。堆无法扩展,因为它已达到最大值。
使用 -XX:+PrintGCDetails 时,GC 日志中会显示空间溢出
怎么避免:
- Increase heap size
- Increase the
-XX:G1ReservePercent=n
, the default is 10. - G1 creates a false ceiling by trying to leave the reserve memory free in case more 'to-space' is desired.
- Increase the
- Start the marking cycle earlier
- Increase the number of marking threads using the
-XX:ConcGCThreads=n
option.