JVM相关 : 2.垃圾回收

2021-09-13  本文已影响0人  lilykeke

1. 如何判断对象可以回收?

1.1 引用计数法

算法:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加一;引用失效时,计数器值减一。计数器为0 的对象时不被使用的。

缺点:无法回收两个相互引用的对象

引用计数.png

1.2 可达性分析算法

可作为 GC Root 对象:

1.3 四种引用

1.强引用

2.软引用

public class SoftReferenceTest {

    public static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        //引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
        for (int i = 0; i < 5; i++) {
            //关联软引用和引用队列 当软引用关联的byte[] 被回收时,软引用自身被加入到引用队列中
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            list.add(ref);
        }
    }
}

3.弱引用

虚引用 和 终结器引用必须配合引用队列

4.虚引用

5.终结器引用

java 编程时不要复写finalize()方法来释放内存,因为效率很低

2. 垃圾回收算法

2.1 标记-清除 Mark-Sweep

算法:首先标记出所有需要回收的对象,标记完成后,统一回收掉所有被标记的对象。
或者,标记出所有存活的对象。清除掉未标记的对象。

优点:

缺点:

标记-清除 (1).png

2.2 标记-整理 Mark-Compack

算法:标记需要回收的对象,让所有存活的对象都向内存空间的一端移动,然后直接清理掉边界以外的内存。

缺点:

优点:

标记-整理.png

2.3 标记-复制 Mark-Copy

算法:将可用内存按容量划分大小相等的两块,每次只使用其中一块。当一块内存用完了,就将存活的对象复制到另一块上面,然后再把已使用的内存空间一次清理掉。

现在的商用java虚拟机大多都优先采用了这种收集算法去回收新生代。
新生代中的对象有98%熬不过第一轮收集,因此并不需要按照1:1的比例来划分新生代的内存空间。

优点:

缺点:

3. 分代垃圾回收

分代垃圾回收.png

特点:

3.1 相关 VM 参数

含义 参数
堆初始大小 -Xms
堆最大大小 -Xmx 或 -XX:MaxHeapSize=size
新生代大小 -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size)
幸存区比例(动态) -XX:InitialSurvivorRatio=ratio + -XX:UserAdaptiveSizePolicy
幸存区比例 -XX:SurvivorRatio=ratio
晋升阈值 -XX:MaxTenuringThreshold=threshold
晋升详情 -XX:+PrintTenuringDistribution
GC详情 -XX:+PrintGCDetails -verbose:gc
Full GC 前 Minor GC -XX:+ScavengeBeforeFullGC

示例:

//-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose gc
public static void main(String[] args) {
     
}

堆信息

Heap
 def new generation   total 9216K, used 1147K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  14% used [0x00000000fec00000, 0x00000000fed1edf0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 2871K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 317K, capacity 384K, committed 384K, reserved 1048576K

3.2 小结

堆内存空间分为较大的 Eden 和 两块较小的 Survivor 区。每次只使用 Eden 和 survivor 中的一块。

这种情况下标记-复制 算法 减少了内存空间的浪费。

复制算法-现作为主流的YGC算法进行新生代垃圾回收

4. 垃圾回收器

垃圾回收器分类:

1.串行

2.吞吐量优先

3.响应时间优先

4.1 串行

开启串行垃圾回收器:

-XX:+UseSerialGC=Serial + SerialOld

为什么工作线程要在安全点停下来?

因为垃圾回收过程中对象的地址可能发生改变,为了保证安全的使用这些对象地址

串行.png

4.2 吞吐量优先

开启吞吐量优先开关

与之有关的一些参数:

-XX:+UseAdaptiveSizePolicy

-XX:GCTimeRatio=ratio

-XX:MaxGCPauseMillis=ms

上面两个设置是冲突的

-XX:ParallelGCTheads=n

垃圾回收线程跟CPU核数相关 ,也可以人为设置最大线程数-XX:ParallelGCTheads=n

吞吐量优先.png

4.3 响应时间优先

开启响应时间优先的开关:CMS

-XX:+UseConcMarkSweepGC~SerialOld -XX:+UseParNewGC

与之有关的一些参数:

-XX:ParallelGCTheads=n -XX:ConcGCThread=threads

-XX:CMSInitiatingOccupancyFraction=percent

-XX:+CMSScavengeBeforeRemark

初始标记时(找到那些跟对象),stop the world

并发标记:从根对象出发,顺着引用链标记其他对象

重新标记时,stop the world

响应时间优先.png

4.4 G1 Garbage First

取代了之前的CMS垃圾回收器

适用场景

相关JVM参数

-XX:+UseG1GC

-XX:G1HeapRegionSize=size

-XX:MaxGCPauseMillis=ms

4.4.1 G1 垃圾回收阶段

G1垃圾回收阶段.png

这是一个循环的过程

4.4.2 Young Collection

划分成一个个大小相等的区域,每个区域都可以独立作为 Eden 、幸存区、老年代

经历一次young collection 伊甸中幸存的对象拷贝到survivor区

幸存区内存不足时拷贝到老年代

G1_youngCollection.png
4.4.3 Young Collection + CM
4.4.4 Mixed Collection

会对E S O 进行全面垃圾回收

-XX:MaxGCPauseMillis:ms


G1MixedCollection.png
4.4.5 Full GC
4.4.6 Young Collection 跨代引用

减少GC Root 的标记时间

G1跨代引用.png
4.4.7 Remark

重新标记阶段

4.4.8 JDK 8u20 字符串去重

-XX:+UseStringDeduplication

String s1 = new String("hello");
String s2 = new String("hello");
4.4.9 JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就知道哪些类不再被使用。当一个类加载器(针对自定义的类加载器)的所有类都不再使用,则卸载它所加载的所有类。

-XX:+ClassUnloadingWithConcurrentMark 默认开启

4.4.10 JDK 8u60 回收巨型对象
4.4.11 JDK9 并发标记起始时间调整
上一篇 下一篇

猜你喜欢

热点阅读