Java面试--垃圾回收机制(GC)

面试题:垃圾回收机制(阿里、三七互娱、商汤、大疆面试题)
面试题:jvm查看gc命令(蘑菇街面试题)
面试题:若是频繁老年代回收怎么分析解决(蚂蚁金服面试题)
面试题:gc的Gengerational collector(携程面试题、阿里面试题)
html

1、要回收哪些区域
在JVM内存模型中,有三个是不须要进行垃圾回收的:程序计数器、JVM栈、本地方法栈。由于它们的生命周期是和线程同步的,随着线程的销毁,它们占用的内存会自动释放,因此只有方法区和堆须要进行GC
2、如何判断对象是否存活
一、引用计数法
引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每一个对象实例都有一个引用计数。当一个对象被建立时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例能够被看成垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。
优势:实现简单,判断效率高
缺点:很难解决对象之间的相互循环引用,因此java语言并无选用引用计数法管理内存
java

public class ReferenceFindTest {
    public static void main(String[] args) {
        MyObject object1 = new MyObject();
        MyObject object2 = new MyObject();

        object1.object = object2;
        object2.object = object1;

        object1 = null;
        object2 = null;
    }
}

这段代码是用来验证引用计数算法不能检测出循环引用。最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,可是因为它们互相引用对方,致使它们的引用计数器都不为0,那么垃圾收集器就永远不会回收它们。
二、可达性分析算法(根搜索算法)
可达性分析算法是从离散数学中的图论引入的,程序把全部的引用关系看做一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点之后,继续寻找这个节点的引用节点,当全部的引用节点寻找完毕以后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被断定为是可回收的对象。
这里写图片描述
在Java语言中,可做为GC Roots的对象包括下面几种:
a) 虚拟机栈中引用的对象(栈帧中的本地变量表);
b) 方法区中类静态属性引用的对象;
c) 方法区中常量引用的对象;
d) 本地方法栈中JNI(Native方法)引用的对象。
3、对象死亡(被回收)前的最后一次挣扎
经过可达性分析,那些不可达的对象并非当即被销毁,他们还有被拯救的机会。
若是要回收一个不可达的对象,要经历两次标记过程。首先是第一次标记,并判断对象是否覆写了 finalize 方法,若是没有覆写,则直接进行第二次标记并被回收。若是对象在finalize()方法中从新与引用链创建了关联关系,那么将会逃离本次回收,继续存活。
方法区如何判断是否须要回收
方法区主要回收的内容有:废弃常量和无用的类。对于废弃常量也可经过引用的可达性来判断,可是对于无用的类则须要同时知足下面3个条件:
① 该类全部的实例都已经被回收,也就是Java堆中不存在该类的任何实例;
② 加载该类的ClassLoader已经被回收;
③ 该类对应的java.lang.Class对象没有在任何地方被引用,没法在任何地方经过反射访问该类的方法。
4、经常使用的垃圾回收算法
一、标记-清除算法
标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,此算法通常没有虚拟机采用。
优势1:解决了循环引用的问题
优势2:与复制算法相比,不须要对象移动,效率较高,并且还不须要额外的空间
不足1:每一个活跃的对象都要进行扫描,并且要扫描两次,效率较低,收集暂停的时间比较长。
不足2:产生不连续的内存碎片
这里写图片描述
二、复制算法
将内存分红两块容量大小相等的区域,每次只使用其中一块,当这一块内存用完了,就将全部存活对象复制到另外一块内存空间,而后清除前一块内存空间。这样一来就不容易出现内存碎片的问题。
一、复制的代价较高,因此适合新生代,由于新生代的对象存活率较低,须要复制的对象较少;
二、须要双倍的内存空间,并且老是有一块内存空闲,浪费空间
这里写图片描述
三、标记-整理算法
思想:在完成标记以后,它不是直接清理可回收对象,而是将存活对象都向一端移动,而后清理掉端边界之外的内存。
不会产生内存碎片,可是依旧移动对象的成本。
这里写图片描述
四、分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不一样的区域。通常状况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区以外还有一个代就是永久代(Permanet Generation)。老年代的特色是每次垃圾收集时只有少许对象须要被回收,而新生代的特色是每次垃圾回收时都有大量的对象须要被回收,那么就能够根据不一样代的特色采起最适合的收集算法。
这里写图片描述
内存被分为下面三个区域:
① 新生代:Enden、form survicor space、to survivor space。
② 老年代
③ 永久代:方法区
新生代的回收算法
包含有Enden、form survicor space、to survivor space三个区,绝大多数最新被建立的对象会被分配到这里,大部分对象在建立以后会变得很快不可达。
① 全部新生成的对象首先都是放在年轻代的。年轻代的目标就是尽量快速的收集掉那些生命周期短的对象。
② 新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。一个Eden区,两个 Survivor区(通常而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,而后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另外一个survivor1区,而后清空eden和这个survivor0区,此时survivor0区是空的,而后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。
③ 当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代。如果老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。
④ 新生代发生的GC也叫作Minor GC,Minor GC发生频率比较高(不必定等Eden区满了才触发)。
老年代的回收算法
① 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。所以,能够认为年老代中存放的都是一些生命周期较长的对象。
② 内存比新生代也大不少(大概比例是1:2),当老年代内存满时触发Major GC即Full GC,Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高。
永久代的回收算法
用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,可是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候须要设置一个比较大的持久代空间来存放这些运行过程当中新增的类。持久代也称方法区。
5、常见的垃圾收集器
这里写图片描述
Serial收集器(复制算法)
新生代单线程收集器,标记和清理都是单线程,优势是简单高效。是client级别默认的GC方式,能够经过-XX:+UseSerialGC来强制指定。
Serial Old收集器(标记-整理算法)
老年代单线程收集器,Serial收集器的老年代版本。
ParNew收集器(中止-复制算法) 
新生代收集器,能够认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。
Parallel Scavenge收集器(中止-复制算法) 
并行收集器,追求高吞吐量,高效利用CPU。吞吐量通常为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数。
Parallel Old收集器(中止-复制算法)
Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先。
CMS(Concurrent Mark Sweep)收集器(标记-清理算法)
高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择。
6、GC是何时触发的(面试最多见的问题之一)
因为对象进行了分代处理,所以垃圾回收区域、时间也不同。GC有两种类型:Minor GC和Full GC。
Minor GC
  通常状况下,当新对象生成,而且在Eden申请空间失败时,就会触发Minor GC,对Eden区域进行GC,清除非存活对象,而且把尚且存活的对象移动到Survivor区。而后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。由于大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,因此Eden区的GC会频繁进行。于是,通常在这里须要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC由于须要对整个堆进行回收,因此比Scavenge GC要慢,所以应该尽量减小Full GC的次数。在对JVM调优的过程当中,很大一部分工做就是对于Full GC的调节。有以下缘由可能致使Full GC:
a) 年老代(Tenured)被写满;
b) 持久代(Perm)被写满;
c) System.gc()被显示调用;
d) 上一次GC以后Heap的各域分配策略动态变化;
面试题:jvm查看gc命令
jstat -gc 12538 5000
即会每5秒一次显示进程号为12538的java进成的GC状况,
面试题:若是频繁老年代回收怎么分析解决(蚂蚁金服面试题)
(我的理解)老年代是存放那些在程序中经历了好几回回收仍然还活着或者特别大的对象(这个大就要看你是否设置了-XX:PretenureSizeThreshold 参数了)。检查程序中是否有比较大的对象,或者这个参数设置是否合理。
web


Java面试的完整博客目录以下:Java笔试面试目录面试


内容引自:https://www.cnblogs.com/1024Community/p/honery.html算法

转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916 若是以为本文对您有帮助,请点击支持一下,您的支持是我写做最大的动力,谢谢。
这里写图片描述
多线程