您的位置:首页 > 职场人生

java垃圾回收 | 克服面试

2018-12-25 11:43 295 查看

一,我们要知道面试官为什么这样问我们

假定碰上了一位理性且有足够知识储备的面试官。一个好的面试过程通常是逐步推进的,面试官要通过不断深入的问题来判断被面试者的知识极限在哪里,所以没有必要担心被问到自己不知道答案的问题——那就是你的极限,然而那个极限或许已经可以满足这个职位了。在被推向极限时,最好能表现出积极思考的状态,有理有据的从一些基本原理出发去推论;此时漫无边际的胡乱回答一通或者畏缩而不回答都容易留下负面印象。

二,了解一下jvm的常用参数

2.1 关于JVM参数必须知道的小知识
JVM参数分为标准参数和非标准参数,所有以-X和-XX开头的参数都是非标准参数,标准参数可以通过java -help命令查看,比如:-server就是一个标准参数。
非标准参数中,以-XX开头的都是不稳定的且不推荐在生成环境中使用。但现在的情况已经有所改变,很多-XX开头的参数也已经非常稳定了,但不管什么参数在使用前都应该了解它可能产生的影响。
布尔型参数,-XX:+表示激活选项,-XX:-表示关闭此选项。
部分参数可以使用jinfo工具动态设置,比如:jinfo -flag +PrintGCDetails 12278,能够动态设置的参数很少,所以用处有限,至于哪些参数可以动态设置,可以参考jinfo工具的使用方法。
2.2 GC日志
GC日志是一个非常重要的工具,它准确的记录了每一次GC的执行时间和结果,通过分析GC日志可以帮助我们优化内存设置,也可以帮助改进应用的对象分配方式。如何阅读GC日志不在本文的范畴内,大家可以参考网上相关文章。

下面几个关于GC日志的参数应该加入到应用启动参数列表中:

-XX:+PrintGCDetails 开启详细GC日志模式
-XX:+PrintGCTimeStamps在每行GC日志头部加上GC发生的时间,这个时间是指相对于JVM的启动时间,单位是秒
-XX:+PrintGCDateStamps在GC日志的每一行加上绝对日期和时间,推荐同时使用这两个参数,这样在关联不同来源的GC日志时很有帮助
-XX:+PrintHeapAtGC输出GC回收前和回收后的堆信息,使用这个参数可以更好的观察GC对堆空间的影响
-Xloggc设置GC日志目录
设置这几个参数后,发生GC时输出的日志就类似于下面的格式 (不同的垃圾收集器格式可能略有差异):

简单的说明:

2018-01-07T19:45:08.627+0800 - GC开始时间
0.794 - GC开始时间相对于JVM启动时间
GC - 用来区分是Minor GC 还是 Full GC,这里是Minor GC
Allocation Failure - GC原因,这里是因为年轻代中没有任何足够空间,也就是分配失败
PSYoungGen - 垃圾收集算法,这里是Parallel Scavenge
153600K->4564K(179200K) - 本次垃圾回收前后年轻代内存使用情况,括号内表示年轻代总大小
153600K->4580K(384000K) - 在本次垃圾回收前后整个堆内存的使用情况,括号内表示总的可用堆内存
0.0051736 secs - GC持续时间
[Times: user=0.01 sys=0.00, real=0.01 secs] - 多个维度衡量GC持续时间

三,垃圾回收

一、垃圾回收机制具有以下的特点:

1、 垃圾回收机制只负责回收堆内存,不会回收任何物理资源
2、 程序无法精确控制垃圾回收的进行,会在合适的时候进行
3、 在垃圾回收机制回收的任何对象之前,总会先调用它的finalize()方法

Java GC什么时候执行?

eden区空间不够存放新对象的时候,执行Minro GC。升到老年代的对象大于老年代剩余空间的时候执行Full GC。调优主要是减少Full GC的触发次数。可以通过NewRatio控制新生代转老年代的比例。通过MaxTenuringThreshold设置对象进入老年代的年龄阀值

按代的垃圾回收机制

新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象被创建在新生代。然后“消失”。对象从这个区域“消失”的过程我们称之为:Minor GC

老年代(old generation):对象没有变得不可达,而且从新生代周期中存活了下来,会被拷贝到这里,其区域分配的空间要比新生代多,也正是由于其相对大的空间。发生在老年代的GC次数要比新生代少得多。对象从老年代中消失的过程,称之为Major GC或者Full GC。

持久代(Permanent generation)也称之为 方法区(Method area):用于保存类常量以及字符串常量。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Major GC 。只不过在这个区域发生GC的条件非常严苛,必须符合以下三种条件才会被回收:

1、所有实例被回收

2、加载该类的ClassLoader 被回收

3、Class 对象无法通过任何途径访问(包括反射)

JVM GC 算法讲解

1、根搜索算法

根搜索算法是从离散数学中的图论引入的,程序把所有引用关系看作一张图,从一个节点GC ROOT 开始,寻找对应的引用节点,找到这个节点后,继续寻找这个节点的引用节点。当所有的引用节点寻找完毕后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

目前Java中可以作为GC ROOT的对象有:

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

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

3、方法区中常亮引用的对象

4、本地方法栈中引用的对象(Native对象)

基本所有GC算法都引用根搜索算法这种概念。

2、标记 - 清除算法

标记-清除算法采用从根集合进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象进行直接回收。

标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活的对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,并没有对还存活的对象进行整理,因此会导致内存碎片。

3、复制算法
复制算法将内存划分为两个区间,使用此算法时,所有动态分配的对象都只能分配在其中一个区间(活动区间),而另外一个区间(空间区间)则是空闲的。

复制算法采用从根集合扫描,将存活的对象复制到空闲区间,当扫描完毕活动区间后,会的将活动区间一次性全部回收。此时原本的空闲区间变成了活动区间。下次GC时候又会重复刚才的操作,以此循环。

复制算法在存活对象比较少的时候,极为高效,但是带来的成本是牺牲一半的内存空间用于进行对象的移动。所以复制算法的使用场景,必须是对象的存活率非常低才行,而且最重要的是,我们需要克服50%内存的浪费。

4、标记 - 整理算法

标记-整理算法采用 标记-清除 算法一样的方式进行对象的标记、清除,但在回收不存活的对象占用的空间后,会将所有存活的对象往左端空闲空间移动,并更新对应的指针。标记-整理 算法是在标记-清除 算法之上,又进行了对象的移动排序整理,因此成本更高,但却解决了内存碎片的问题。

总结很重要:JVM为了优化内存的回收,使用了分代回收的方式,对于新生代内存的回收(Minor GC)主要采用复制算法。而对于老年代的回收(Major GC),大多采用标记-整理算法。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: