Java学习笔记-JVM垃圾回收

二、垃圾回收机制(GC)

垃圾回收主要针对堆与方法区(比较少)来进行。

判断对象是否可被回收

1. 引用计数算法

为对象添加一个引用计数器,引用计数为0的对象可被回收。

存在的问题:如果两个对象出现循环引用时,无法被回收(a引用b对象,b对象引用a对象/死锁)。造成内存泄露

2. 可达性分析算法

以GC Root为起点进行搜索,不可达的对象可被回收。
通过使用Eclipse Memory Analyzer,得到的有 系统类、本地方法栈、锁对象和活动线程。

具体来说,GC Root内容包括:

  1. 虚拟机栈(栈帧中的局部变量区)中引用的对象;
  2. 本地方法栈中JNI(Native方法)引用的对象;
  3. 方法区中(PS部分)类静态属性引用的对象;
  4. 方法区中的常量引用的对象。

PS:在JDK1.8版本中,串池与静态变量都存放至堆中,便于垃圾回收。

Java的引用类型

在这里插入图片描述

1. 强引用

被强引用关联的对象不 会被回收。(即使出现OOM也不会被回收
使用 new 关键字来创建强引用。

2. 软引用

被引用关联的对象只有在内存不够的情况下才会被回收(与该引用对象得到的时间,当前堆可用内存大小都有关系)。(可以配合引用队列来释放该引用的空间占用)
使用 SoftReference类来创建软引用。

Object ojb = new Object();
SoftReference<object> w = new SoftReference<object>(obj);
obj = null 
// 软引用案例 使用 -Xmx20m -XX:+PrintGCDetails -verbose:gc
private static final int _4MB = 4*1024*1024;

public static void main(String[] args){
    List<SoftReference<byte[]>> list = new ArrayList<>();
    for (int i=0;i<5;i++){
        SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
        System.out.println(ref.get());
        list.add(ref);

        System.out.println(list.size());
    }

    // 使用软引用,前面的3条都会被回收掉
    for(SoftReference<byte[]> ref : list)
        System.out.println(ref.get());
}
应用

如果有一个应用需要读取大量的本地图片:

  1. 如果每次读取图片都需要从硬盘读取的话,会严重影响性能。
  2. 如果一次性加载到内存则会造成内存溢出。

针对该情况,可以设计一个HashMap<String, SoftReference> 来存储路径字符串与图片对象关联的软引用之间的映射关系,内存不足时,JVM会自动回收图片对象占用的空间,有效避免了OOM问题。

3. 弱引用

被弱引用关联的对象只能存活到下一次垃圾回收发生之前。
(实际中如果空间不够则会回收最近的弱引用所引用的对象)
(可以配合引用队列来释放该引用的空间占用)
使用 WeakReference类进行弱引用的创建。

WeakHashMap

当key不再被使用(置null)时,其对应的Entry对象会被垃圾回收掉。

4. 虚引用

一个对象的虚引用的存在与否不会对原始对象的生存时间造成影响,也无法通过虚引用来得到一个对象。(在JVM对直接内存的回收ByteBuffer时有应用)
虚引用引用的对象被垃圾回收时,虚引用对象自身会被放入引用队列。由其它线程(保护线程ReferenceHandler)去调用虚引用对象的方法。
使用PhantomReference来创建虚引用。

5. 终结器引用(不推荐)

当对象重写了终结方法 finalize() ,并且没有强引用对该类进行引用时,虚拟机会创建一个对象的终结器引用。

当该对象要被回收时,终结器引用先被放入引用队列,由一个优先级很低的线程来查看待回收对象中是否包含终结器引用,如果有,则调用对象的finalize() 方法,等下一次垃圾回收时才会被回收。

JVM常见错误/异常

  1. StackOverflowError

  2. OutOfMemoryError: Java heap space

  3. OutOfMemoryError: GC overhead limit exceeded(JVM垃圾回收时间过长)

  4. OutOfMemoryError: Direct buffer memory(直接内存溢出,DirectByteBuffer)

  5. OutOfMemoryError: unable to create new native thread(高并发请求服务器时易出现)
    原因: 一个应用进程创建多个线程,超过系统承载极限(linux默认是1024个,一般上限为默认值的2/3已经很高了)

  6. OutOfMemoryError: Metaspace

垃圾回收算法

标记清除(老年代)

先标记要回收的对象,然后进行统一回收。

优点:处理速度较快。
缺点:易产生内存碎片。

标记整理(老年代)

先标记要回收的对象,然后再次扫描,往一端移动存活对象。

优点:不会产生内存碎片;
缺点:处理速度较慢。

复制(新生代)

只有一个阶段,将内存区划分为大小相等的两个区域,一个区域空闲(TO区),一个被使用(FROM);在进行垃圾回收时,会将FROM中使用的空间放入TO中,同时不会产生碎片,在交换完成后,交换FROM与TO区域(指针交换)。

优点:不会有内存碎片;
缺点:需要占用双倍的内存空间。

分代的垃圾回收机制(Minor gc)

在这里插入图片描述

  1. 复制
    首先,Eden区满了后会触发第一次gc,此时会把还存活的对象拷贝到SurvivorFrom区;当Eden再次满了时,则会对Eden和SurvivorFrom两个区域进行垃圾回收,此时还存活的对象会被复制到SurvivorTo区,同时这些对象年龄+1。(如果对象年龄超过阈值 [阈值由JVM参数 MaxTenuringThreshold 决定,默认15] 或对象过大,会被放入老年代)
  2. 清空
    进行复制操作后,Eden和SurvivorFrom区会被清空。
  3. 互换
    最后,SurvivorFrom与SurvivorTo区域进行交换。

PS:一个线程内的OOM(OutofMemoryError)并不会导致主线程的结束。

垃圾回收器

在这里插入图片描述
对应的连线表示新生代与老年代垃圾回收器的搭配方式。

  1. 串行(Serial, 1:1):单线程,适用于堆内存较小,单核CPU。(新生代)
    在这里插入图片描述

  2. ParNew(N:1):新生代使用并行垃圾回收器,老年代默认使用SerialOldGC。但是目前最常见的是配合老年代的CMS工作。

    新生代使用的是复制算法;
    老年代使用的是标记-整理算法;
    在这里插入图片描述

  3. Parallel Scavenge(N:N):新生代老年代都使用ParallelGC。高吞吐量(利用CPU的时间在总时间的占比),同时采用自适应调节策略动态调整参数。

  4. 响应时间优先(CMS,并发标记清除):多线程,可以与用户线程并发执行,适用于堆内存较大,多核CPU。让单次的STW时间最短。(用于老年代)

存在的问题:由于采用的是标记清除算法,因此垃圾回收时会产生很多内存碎片,导致并发失败,退化为串行垃圾回收,造成了垃圾回收时间较长。

CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败。
在这里插入图片描述
一般使用 ParNew(新生代)+CMS(老年代)+SerialOld(用于CMS出错时的后备收集器)组合。

G1(Garbage First)

堆内存会被划分为很多固定大小的区域,每个区域分别被标记为E(伊甸园),S(幸存者),O(老年代)和H(巨型区域,存放的对象大小大于等于区域的一半)。(区域不再连续

优点
  1. 使用的是内存整理算法,不会产生太多内存碎片。
  2. STW更可控,用户可以指定期望停顿的时间。
  3. 区域化内存划分,避免了全内存区的GC操作。
适用场景
  1. 同时注重吞吐量与低延迟,默认暂停目标是200ms。
  2. 适用于超大堆内存,会将堆划分为多个大小相等的区域。(region)
  3. 整体上为标记-整理算法,区域之间使用的是复制算法。
相关JVM参数

-XX:+UseG1GC // JDK9默认打开
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time

回收流程(类似CMS)
  1. 初始标记(STW):标记直接可达对象(根对象);
  2. 并发标记:寻找存活对象;(老年代空间比例达到阈值时发生)
  3. 最终标记:标记并发标记阶段发生变化的对象,用于回收;
  4. 筛选回收:对各区域回收价值进行评估, 使用Young GC或Mixed GC进行垃圾回收。(CMS使用的是标记清除)
Young GC

每次回收所有的伊甸园区与幸存者区,并将存活对象复制到老年代以及另外一部分幸存者区。

Mixed GC

除了回收整个新生代,还会回收部分老年代,并且可以进行选择性回收(选择回收价值最高的进行回收)。
当垃圾回收速度跟不上垃圾产生速度时,退化为串行垃圾回收(进行 full gc)。

JVMGC与SpringBoot微服务生产部署与调参优化

java -server [parameters] -jar package.jar/war

parameters中使用JVMGC参数进行调优。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值