jvm垃圾回收(区域、时间、算法)

1、进行垃圾回收的区域

(1)堆

(2)方法区

栈是线程的私有数据,所以不进行垃圾回收

2、垃圾回收的时间

对这个对象不再引用的时候

public class ReferenceCountingGC {
    private static final int MB=1024*1024;
    public Object instance=null;
    private byte[] size=new byte[2*MB];

    public static void main(String[] args) {
        ReferenceCountingGC referenceCountingGC1=new ReferenceCountingGC();
        ReferenceCountingGC referenceCountingGC2=new ReferenceCountingGC();
        //循环引用
        referenceCountingGC1.instance=referenceCountingGC2;
        referenceCountingGC2.instance=referenceCountingGC1;

        referenceCountingGC1=null;
        referenceCountingGC2=null;

        System.gc();;
    }
}

(1)引用计数

给对象添加一个引用计数器,每当这个对象进行一次引用,计数器就加1;每当引用失效的时候,计数器就减1。当这个计数器等于0的时候,表示这个对象不会再被引用了,可以作为垃圾进行回收。但是,存在循环引用的时候会导致内存泄漏

3、java的引用类型

(1)强引用

Object obj=new Object();这样的常规引用,只要引用还在就永远不会回收对象,在代码中有明显的new Object()这类引用,只要这种引用还在,垃圾回收器就不会回收它。就算内存不够,抛出OutOfMrmory异常也不会回收对象。

(2)弱引用

生存到下一次垃圾回收之前,无论当前内存是否够用,都回收掉被弱引用关联的对象

(3)软引用

在发生内存溢出之前,进行回收,如果这次回收之后还没有足够的内存,则报OOM。如果内存够用的情况下,不会回收,如果内存不够的话进行

(4)虚引用

不会对对象的生命周期有任何影响,也无法通过它得到对象的实例,唯一的作用也就是在垃圾回收之前收到一个系统通知

4、可达性分析算法

(1)可达与不可达

可达:
在这里插入图片描述

不可达:

在这里插入图片描述

此时对象已经不再被引用了,也就是说可以被回收了。

(2)在java语言中,可以作为GC Roots的对象(肯定不会变成垃圾被回收的对象)包括下面几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI(即一般说的Native方法)引用的对象

5、标记、清除算法

(1)概念:

该算法包括“标记”和“清除”两个阶段:首先标记出所有要回收的对象,在标记完成后统一回收所有被标记的对象。

(2)缺点:

效率问题,标记和清除的效率都不高

空间问题,标记和清除之后会产生大量不连续的内存碎片

6、复制算法

(1)概念

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

(2)缺点

内存缩小为原来的一半,内存利用率太低(还要留出另外一半等着复制的时候使用)

(3)优化

为了克服该算法的缺点对该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存叫Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor区不够,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代)

7、标记整理算法

(1)概念

标记过程依旧和“标记清除算法”一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后把其他的内存区域清理掉。可以消除内存碎片,但是整理的时候会降低算法的效率

8、分代算法

(1)概念

根据对象存活周期的不同将内存划分为几块

(2)回收原则

一般是把java堆分为新生代和老年代,这样可以根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾回收的时候都会回收掉大量的对象,只有少量的对象存活,此时,选用复制算法,只需要付出少量存活对象的复制成本即可完成对象的收集,而老年代中因为对象的存活率高,没有额外空间进行分配担保,就必须使用“标记清理”或“标记整理”算法进行回收。新生代垃圾回收较为频繁,老年代不频繁

堆:

在这里插入图片描述

经过几次回收之后会进入老年代

(3)分区

伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。当伊甸区满的时候会触发Minor-GC,采用复制算法将伊甸区的幸存对象和from存活的对象复制到幸存区的to区域,存活的对象年龄加一,当在新生代的生存次数累积到一定程度(最大寿命是15,存储寿命的地方是4bit)可以进入老年代。

幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。

终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时(老年代空间不足,先尝试触发Minor-GC,当Minor-GC也不能腾出足够的空间的时候),就会触发一次完全收集(Major-GC或Full-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。

如果老年代仍旧不能分配出来空间,就会报异常

(4)STW

当进行Minor-GC或Full-GC的时候会触发stop the world把用户的其他线程暂停掉,因为Minor-GC的时候牵涉到对象的复制,如果还有其他线程操作该对象就会引起混乱。Full-GC的时间更长,因为它采用标记整理算法,速度更慢。

(5)虚拟机相关参数
在这里插入图片描述

9、GC类型

(1)保守式GC

在回收的时候,不对数据进行记录,而是扫描内存,效率低

(2)半保守式GC

在类里面记录信息,然后扫描

(3)精确式GC

OopMap存放运行信息(类型、地址等),回收的时候只需查询Map即可

10、安全点:safepoint

(1)概念

在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots的枚举,但如果每一条指令都生成对应的OopMap那将会需要大量的额外空间,这样GC的空间成本将会变得很高

(2)抢占式中断

在GC发生时,首先把所有的线程全部中断,如果发现有中断的地方不在安全点上,就恢复线程,让它跑到安全点上,必须要等到Java线程都进入到safepoint的时候VMThread才能开始执行GC

位置:

循环的末尾(防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint)
方法返回前
调用方法的call之后
抛出异常的位置

缺点:处于sleep或wait的时候不能到达安全点

(3)主动式中断

当GC需要中断线程的时候,不直接对线程进行操作,仅仅简单地设置一个标志,发现中断标志为真的时候就自己中断挂起

11、相关参数

与垃圾回收相关的JVM参数:

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小

-Xmn — 堆中年轻代的大小

-XX:-DisableExplicitGC — 让System.gc()不产生任何作用

-XX:+PrintGCDetail — 打印GC的细节

-XX:+PrintGCDateStamps — 打印GC操作的时间戳

12、程序分析

(1)jvm参数

在这里插入图片描述

最大堆空间:20M

打印GC详情

新生代:10M

垃圾回收器:SerialGC

(2)不对存储区进行任何操作:

public class Test3 {
    private static final int _512KB=512*1024;
    private static final int _1MB=1024*1024;
    private static final int _6MB=6*1024*1024;
    private static final int _7MB=7*1024*1024;
    private static final int _8MB=8*1024*1024;

    public static void main(String[] args) {

    }
}

测试结果:

Heap
 def new generation   total 9216K, used 2025K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  24% used [0x00000000fec00000, 0x00000000fedfa778, 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 3224K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

堆内存:

新生代:def new generation

老年代:tenured generation

(3)将7M的对象放入堆内存:

public class Test3 {
    private static final int _512KB=512*1024;
    private static final int _1MB=1024*1024;
    private static final int _6MB=6*1024*1024;
    private static final int _7MB=7*1024*1024;
    private static final int _8MB=8*1024*1024;

    public static void main(String[] args) {
        ArrayList<byte[]> list=new ArrayList<>();
        list.add(new byte[_7MB]);
    }
}

测试:

[GC (Allocation Failure) [DefNew: 1861K->641K(9216K), 0.0026541 secs] 1861K->641K(19456K), 0.0283396 secs] [Times: user=0.00 sys=0.00, real=0.03 secs] 
Heap
 def new generation   total 9216K, used 8055K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  90% used [0x00000000fec00000, 0x00000000ff33d8c0, 0x00000000ff400000)
  from space 1024K,  62% used [0x00000000ff500000, 0x00000000ff5a0710, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3229K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

因为发生了Minor-GC,幸存代的空间也被占用了

(4)放入8M的对象

public class Test3 {
    private static final int _512KB=512*1024;
    private static final int _1MB=1024*1024;
    private static final int _6MB=6*1024*1024;
    private static final int _7MB=7*1024*1024;
    private static final int _8MB=8*1024*1024;

    public static void main(String[] args) {
        ArrayList<byte[]> list=new ArrayList<>();
        list.add(new byte[_7MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_512KB]);

    }
}

测试:

[GC (Allocation Failure) [DefNew: 1861K->614K(9216K), 0.0029223 secs] 1861K->614K(19456K), 0.0029845 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 8621K->514K(9216K), 0.0059726 secs] 8621K->8292K(19456K), 0.0060135 secs][Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 1191K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   8% used [0x00000000fec00000, 0x00000000feca93e0, 0x00000000ff400000)
  from space 1024K,  50% used [0x00000000ff400000, 0x00000000ff480848, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 7778K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  75% used [0x00000000ff600000, 0x00000000ffd98a48, 0x00000000ffd98c00, 0x0000000100000000)
 Metaspace       used 3214K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 348K, capacity 388K, committed 512K, reserved 1048576K

此时,老年代也有占用,一部分对象已经晋升到老年代

(5)大对象直接晋升老年代(新生代放不下)

public class Test3 {
    private static final int _512KB=512*1024;
    private static final int _1MB=1024*1024;
    private static final int _6MB=6*1024*1024;
    private static final int _7MB=7*1024*1024;
    private static final int _8MB=8*1024*1024;

    public static void main(String[] args) {
        ArrayList<byte[]> list=new ArrayList<>();
        list.add(new byte[_8MB]);
    }
}

测试:

Heap
 def new generation   total 9216K, used 2025K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  24% used [0x00000000fec00000, 0x00000000fedfa778, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 8192K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  80% used [0x00000000ff600000, 0x00000000ffe00010, 0x00000000ffe00200, 0x0000000100000000)
 Metaspace       used 3229K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

无GC,直接进入老年代

(6)放入两个8M的对象:新生代和老年代都放不下

public class Test3 {
    private static final int _512KB=512*1024;
    private static final int _1MB=1024*1024;
    private static final int _6MB=6*1024*1024;
    private static final int _7MB=7*1024*1024;
    private static final int _8MB=8*1024*1024;

    public static void main(String[] args) {
        ArrayList<byte[]> list=new ArrayList<>();
        list.add(new byte[_8MB]);
        list.add(new byte[_8MB]);
    }
}

测试:

[GC (Allocation Failure) [DefNew: 1861K->616K(9216K), 0.0016678 secs][Tenured: 8192K->8807K(10240K), 0.0274991 secs] 10053K->8807K(19456K), [Metaspace: 3201K->3201K(1056768K)], 0.0498951 secs] [Times: user=0.00 sys=0.02, real=0.05 secs] 
[Full GC (Allocation Failure) [Tenured: 8807K->8789K(10240K), 0.0025803 secs] 8807K->8789K(19456K), [Metaspace: 3201K->3201K(1056768K)], 0.0026219 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at pers.zhb.test.Test3.main(Test3.java:15)
Heap
 def new generation   total 9216K, used 410K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   5% used [0x00000000fec00000, 0x00000000fec66800, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 8789K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  85% used [0x00000000ff600000, 0x00000000ffe95630, 0x00000000ffe95800, 0x0000000100000000)
 Metaspace       used 3255K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 353K, capacity 388K, committed 512K, reserved 1048576K
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值