06-垃圾回收器

什么是垃圾回收器?

垃圾回收器是垃圾回收算法的具体实现,不同商家,不同版本的jvm所提供的垃圾收集器可能会存在很大的差别

 

新生代的垃圾回收器:Serial,parNew,Parallel Scavenge

老年代的垃圾回收器:CMS,Serial Old,Parallel Old

整堆收集器:G1垃圾回收器

 

可以作为GCROOT的对象?

       a.虚拟机栈中的引用对象

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

      c.方法区中的常量引用的对象

      d.本地方法栈中JNI引用的对象

 

 

 

jvm常用命令行调优工具?

jps:负责查看java进程

jinfo:负责查看jvm参数和动态修改jvm参数的命令

jstat:查看jvm运行时的状态信息,包括内存状态和垃圾回收

jstack:查看jvm线程快照(对栈堆进行追踪)

jmap:生成堆dump文件(对内存生成快照)
 

 

Minor GC 和Major GC的区别?

Minor GC:又称作新生代GC,指的是在新生代发生的垃圾收集操作,Minor GC非常频繁,回收速度也较快

Major GC:又称作Full GC或者老年代GC,指的是发生在老年代的GC,出现Full GC经常会伴随着至少一次的Minor GC

Major GC速度一般比Minor GC慢10倍以上

 

Serial收集器?

Serial(串行)垃圾收集器是最基本,发展历史最悠久的收集器

特点:针对新生代,复制算法,单线程收集(进行垃圾回收时,必须暂停所有工作线程,直到完成)

应用场景:Serial依然是HotSpot在Client模式下的默认的新生代收集器

优点:简单高效,对于单个cpu来说,由于Serial没有线程切换的开销,所以可以获得最高的单线程收集效率

设置参数:"-XX:+UseSerialGC":添加该参数来指定使用哪个垃圾收集器

 

Stop the World?

Jvm在后台自动发起和自动完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉,即GC停顿,会带用户不好的体验。直到现在,用户的停顿时间不断地缩短,但是依然没有全完消除

 

ParNew收集器?

ParNew垃圾收集器就是Serial的多线程版本

除了多线程以外,ParNew与Serial一模一样

在Server模式下,ParNew收集器是一个非常重要的垃圾收集器,因为除了Serial外,只有ParNew能和CMS收集器配合工作

但在单个CPU环境下,不会比Serial效果好,因为存在线程切换的时间开销

设置参数:

-XX:UseConcMarkSweepGC   指定使用Cms后,会默认使用ParNew作为新生代收集器

-XX:UseParNewGC  强制使用ParNew收集器

-XX:ParallelGCThread 指定垃圾收集的线程数量,ParNew默然开启的收集线程与CPU的数量相同

 

Parallel Scavenge收集器?

Parallel Scavenge垃圾收集器与吞吐量关系密切,所以也成称吞吐量收集器

特点:新生代收集器,复制算法,多线程收集  CMS等收集器的主要关注点是缩短垃圾回收时用户的等待时间,而Parallel Scavenge的关注点是达到一个可控制的吞吐量

应用场景:高吞吐量为目标,让用户代码获得更长的运行时间,当应用程序运行在多个cpu上,对暂停时间没有特别高的要求时

 

垃圾收集的时间是新生代和老年代收集的总时间

吞吐量 = 用户代码运行时间/(用户代码运行时间+垃圾收集时间)

 

Serial Old?

Serial Old收集器是Serial的老年代版本

特点:针对老年代,标记-整理算法,单线程收集

应用场景:主要用于Client模式

 

Parallel Old?

Parallel Old收集器是Parallel Scavenge的老年代版本

特点:针对老年代,标记-整理算法,多线程收集

应用场景:用来代替Serial Old的老年代收集器

设置参数:-XX:+UseParallelOldGC

 

CMS收集器(重点)?

并发标记清理收集器也称作并发停顿收集器或低延迟垃圾收集器

特点:老年代,多线程,标记-清除算法,以获取最短停顿时间为目标,并发收集,低停顿的

应用场景:用户交互频繁,给用户带来较好的体验

运作过程:a:初始标记 仅标记一下GC Roots能直接关联到的对象,速度很快,仍然有停顿

                 b:并发标记     从初始阶段标记的对象中找出所有存活的对象,和用户线程并发运行。运行期间会有新生代的对象晋升到老年代,或是直接在老年代分配对象,或是更新老年代的对象的引用关系,对于这些对象需要重新标记。并发标记只负责将引用发生改变的Card标记为Dirty状态,不负责处理

              c:重新标记  为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录 需要停顿,停顿时间比初始标记长,但远比并发标记短,这里采用了多线程并行执行来提升效率

             d:并发清理     回收所有的垃圾对象

整个过程中最耗时的并发标记和并发清除都可以与用户线程一起工作,所以,总体上来说,是并发执行的

缺点:对CPU资源非常敏感,会导致吞吐量降低,应用程序变慢 无法处理浮动垃圾(在并发清除时,用户线程新产生的垃圾)

默认收集线程 = (CPU数量+3)/4

产生大量的内存碎片 由于cms基于标记-清除算法,清除后不会进行压缩操作,因此会产生大量的不连续的内存碎片,导致大对象无法找到适合的连续内存空间,因为会触发一次Full GC,而触发一次Full GC耗时是很多的,就会变慢,解决方案:-XX:+UseCMSCompactAtFullCollection   会使得出现上面的状况时不触发Full GC,而是合并整理内存碎片,但是合并整理过程无法并发,会使停顿时间加长,降低用户体验(默认开启,不会默认进行,需要设置-XX:+CMSFullGCsBeforeCompaction  设置执行多少次Full GC后来一次压缩整理,默认为0)

 

标记清除算法?

   标记清除算法是一种两阶段对对象进行回收的算法

     第一阶段:标记,从根节点出发遍历对象,对访问过的对象打上标记,表示对象可达

     第二阶段:清除,对于那些没有被标记的对象进行回收

  期间对象的内存分配:标记清除算法中新建对象分配内存时假设大小为size,那么需要对空闲列表(free_list)进行一次单向遍历找出大于等于size 的块,有三种分配策略:

                                              1.First_list:找到大于等于size的块后立即返回

                                               2.Best_fit:遍历整个空闲列表,返回大于等于size的最小分块

                                              3.Worst_fit:遍历整个空闲列表,找到最大的分块,切成两部分,一部分是size,然后返回该部分

     

 

 

为什么垃圾回收回收的是堆,而不用回收栈?

    因为栈里面的对象的生命周期和线程同步,随线程的销毁而销毁,所以当线程销毁时栈占用的内存就会自动释放

 

 

空间分配担保失败机制?

     1.准备在新生代进行minor GC时会首先检查老年代的最大连续空间区域的大小是否大于新生代所有对象的大小

     2.如果老年代能装下所有新生代对象,那么minor GC没有风险就会进行minor GC

    3.如果老年代无法装下,垃圾收集器会进行一次预测:根据以往minor GC过后存活对象的平均数来预测这次minor GC后存活对象的平均数。这个时候就要看是否允许冒险,允许的话直接新生代进行回收,不允许的话那么老年代会触发full gc

 

 

堆和栈的区别?

    所有对象的空间分配都是在堆上进行的,堆随着jvm的启动创建,随着jvm的关闭而销毁,栈里面存储是局部变量,栈是随着线程的创建而创建,随着线程的销毁而销毁的,并且垃圾回收不需要在栈上进行

 

Callable和Runnable的区别?

   相同点是两者都是接口,都需要调用thread.start方法启动线程

  不同点是callable允许返回值,而Runnable没有返回值,callable可以抛出异常,Runnable不能抛出异常,callable和Runnable都可以应用于executors。但是thread类只支持Runnable

 

为什么新生代用复制算法,老年代用标记整理和标记清除算法,不同的算法的不同的优点?

    首先,因为新生代的垃圾回收是很频繁的,并且新生代需要清理的对象数量很多,假如采用标记整理算法,需要大量的移动操作,时间复杂度很高,而复制算法,不需要移动,而是直接将存活对象进行复制大大减少了时间复杂度,所以新生代采用复制算法。不过缺点就是浪费了一半的内存,并且如果对象的存活率很高,那么需要将所有对都复制一遍,造成的开销特别大,而新生代的对象的存活时间都不是很长的,所以在新生代使用复制算法正合适

    接下来,为什么老年代不使用复制算法呢?也正是因为老年代中都是一些大对象,一些存活时间长的对象,所以假如使用复制算法,那么开销就很大。并且老年代没有空间分配担保失败机制,所以就使用了标记整理或者标记清除算法来进行回收

 

新生代为什么有两个Survivor区?

  首先,Survivor区存在的意义就是减少被送到老年代的对象,进而减少full gc的发生,Survivor的预筛选保证只有经历16次minor gc还能在新生代中存活的对象才会被送入老年代

  设置两个Survivor区的最大好处就是解决了碎片化。

           如果只有一个Survivor区:刚刚新建的对象在eden区中,一旦eden区满了就会触发一次minor gc,eden中的存活对象就会被送到Survivor区,当下一次eden区也满了时候,此时进行minor gc,eden和Survivor区都会各有一些存活对象,如果此时把eden区的存活对象放到Survivor区,那么两部分对象所占用的内存就不是连续的,就会导致内存碎片化。

         而有了两个Survivor区后,第一次eden区满了会将minor gc后存活的对象放到Survivor0,然后清空eden区,当eden又满了后再触发一次minor gc,这个时候将eden区存活的对象和Survivor区存活的对象都复制到Survivor1中去,也就解决了碎片化问题

 

jdk8默认使用的垃圾回收器?

    新生代:parallel  Scavenge

   老年代:parallel old

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值