JVM面试整理二

8、请你说说内存溢出?

内存溢出,简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。 引起内存溢出的原因有很多种,常见的有以下几种:

1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;

2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;

3. 代码中存在死循环或循环产生过多重复的对象实体;

4. 使用的第三方软件中的BUG;

5. 启动参数内存值设定的过小。

9、请你说说内存泄漏?

内存泄漏,是指不再使用的对象仍然被引用,导致垃圾收集器无法回收它们的内存。由于不再使用的对象仍然无法清理,甚至这种情况可能会越积越多,最终导致致命的OutOfMemoryError。 可以按照如下的思路来分析和解决内存泄漏问题:

1. 启用分析器 Java分析器是通过应用程序监视和诊断内存泄漏的工具,它可以分析我们的应用程序内部发生的事情,例如如何分配内存。使用分析器,我们可以比较不同的方法并找到可以最佳利用资源的方式。

2. 启用详细垃圾收集日志 通过启用详细垃圾收集日志,我们可以跟踪GC的详细进度。要启用该功能,我们需要将以下内容添加到JVM的配置当中:`-verbose:gc`。通过这个参数,我们可以看到GC内部发生的细节。

3. 使用引用对象 我们还可以借助java.lang.ref包内置的Java引用对象来规避问题,使用java.lang.ref包,而不是直接引用对象,即使用对象的特殊引用,使得它们可以轻松地被垃圾收集。

4. Eclipse内存泄漏警告 对于JDK1.5以及更高的版本中,Eclipse会在遇到明显的内存泄漏情况时显示警告和错误。因此,在Eclipse中开发时,我们可以定期地访问“问题”选项卡,并更加警惕内存泄漏警告。

5. 基准测试 我们可以通过执行基准测试来衡量和分析Java代码的性能。通过这种方式,我们可以比较执行相同任务的替代方法的性能。这可以帮助我们选择更好的方法,并可以帮助我们节约内存。

6. 代码审查 最后,我们总是采用经典的老方式来进行简单的代码演练。在某些情况下,即使这种看似微不足道的方法也有助于消除一些常见的内存泄漏问题。 加分回答-没有一刀切的解决方案,具体问题具体分析 通俗地说,我们可以将内存泄漏视为一种疾病,它通过阻塞重要的内存资源来降低应用程序的性能。和所有其他疾病一样,如果不治愈,随着时间的推移,它可能导致致命的应用程序崩溃。 内存泄漏很难解决,找到它们需要对Java语言有很深的理解并掌握复杂的命令。在处理内存泄漏时,没有一刀切的解决方案,因为泄漏可能通过各种不同的事件发生。 但是,如果我们采用最佳实践并定期执行严格的代码演练和分析,那么我们就可以将应用程序中内存泄漏的风险降到最低。

10、JVM中一次完整的GC流程是怎样的?

1、首先,任何新对象都分配到 eden 空间。两个幸存者空间开始时都是空的。

2、当 eden 空间填满时,将触发一个Minor GC(年轻代的垃圾回收,也称为Young GC),删除所有未引用的对象,大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年代。

3、所有被引用的对象作为存活对象,将移动到第一个幸存者空间S0,并标记年龄为1,即经历过一次Minor GC。之后每经过一次Minor GC,年龄+1。GC分代年龄存储在对象头的Mark Word里。 4、当 eden 空间再次被填满时,会执行第二次Minor GC,将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1并年龄加1,此时S0变为空。

5、如此反复在S0和S1之间切换几次之后,还存活的年龄等于15的对象(JDK8默认15,JDK9默认7,-XX:InitialTenuringThreshold=7)在下一次Minor GC时将放到老年代中。

6、当老年代满了时会触发Major GC(也称为Full GC),Major GC 清理整个堆 – 包括年轻代和老年代。

11、说说JVM的垃圾回收机制?

依据分代假说理论,垃圾回收可以分为: 新生代收集、老年代收集、混合收集、整堆收集 当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则。而分代收集理论,建立在如下三个分代假说之上,即弱分代假说、强分代假说、跨代引用假说。依据分代假说理论,垃圾回收可以分为如下几类:

1. 新生代收集:目标为新生代的垃圾收集。

2. 老年代收集:目标为老年代的垃圾收集,目前只有CMS收集器会有这种行为。

3. 混合收集:目标为整个新生代及部分老年代的垃圾收集,目前只有G1收集器会有这种行为。

4. 整堆收集:目标为整个堆和方法区的垃圾收集。

加分回答-垃圾收集器 HotSpot虚拟机内置了很多垃圾收集器,其中针对新生代的垃圾收集器有Serial、ParNew、Parallel Scavenge,针对老年代的垃圾收集器有CMS、Serial Old、Parallel Old。此外,HotSpot还内置了面向整堆的G1收集器。 在上述收集器中,常见的组合方式有:

1. Serial + Serial Old,是客户端模式下常用的收集器。

2. ParNew + CMS,是服务端模式下常用的收集器。

3. Parallel Scavenge + Parallel Old,适用于后台运算而不需要太多交互的分析任务。

12、说说GC的可达性分析算法?

可达性分析算法: 以根对象集合(GC Roots)的每个跟对象为起始点,根据引用关系向下搜索,将所有与GC Roots直接或间接有引用关系的对象在对象头的Mark Word里标记为可达对象,即不需要回收的有引用关系对象。搜索过程所走过的路径称为“引用链” 。

GC Roots:即GC根节点集合,是一组必须活跃的引用。可作为GC Roots的对象:

栈引用的对象:Java方法栈、本地方法栈中的参数引用、局部变量引用、临时变量引用等。临时变量是方法里的中间操作结果。

方法区中常量、静态变量引用的对象;

所有被同步锁持有的对象;

所有线程对象;

所有跨代引用对象;

JVM内部的引用:如基本数据类型对应的Class对象,常驻的异常对象,以及应用程序类类加载器;

非可达对象被回收需要两次标记

1、第一次标记后筛选非可达对象:第一次被标记后,会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,也就是是否有机会自救。假如对象没有覆盖或者已被JVM调用过finalize()方法,也就是说不想自救或已自救过,那么此对象需要被回收;假如对象覆盖并没被JVM调用过finalize()方法,该对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法。

2、第二次标记F-Queue里的未自救对象:稍后,收集器将对F-Queue中的对象进行第二次小规模的标记。如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this)赋值给某个引用类型的类变量或者对象的成员变量,那在第二次标记时它将被移出“即将回收”的F-Queue。如果对象这时候还没有逃脱,那基本上它就真的要被回收了。 finalize()方法: finalize()方法是对象逃脱死亡命运的最后一次机会,需要注意的是,任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行。 另外,finalize()方法的运行代价高昂,不确定性大,无法保证各个对象的调用顺序,如今已被官方明确声明为不推荐使用的语法。

当前主流的商用程序语言的内存管理子系统,都是通过可达性分析算法来判定对象是否存活的。 这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

GC Roots 到底是什么东西呢,哪些对象可以作为 GC Root 呢?

是一组必须活跃的引用。在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

*在虚拟机栈中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等;

在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量;

在方法区中常量引用的对象,譬如字符串常量池里的引用;

在本地方法栈中引用的对象;

JVM内部的引用,如基本数据类型对应的Class对象,常驻的异常对象,以及系统类加载器;

所有被同步锁持有的对象;

反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

13、说说JVM的垃圾回收算法?

1. 标记清除算法

算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。它主要有如下两个缺点: 第一个是执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低。 第二个是内存空间碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当程序在运行过程中需要分配较大对象时无法找到足够的连续的内存而不得不提前触发另一次垃圾收集。

2. 标记复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。

这种复制回收算法的代价是将可用内存缩小为了原来的一半,空间浪费未免太多了一点。另外,如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销。所以,现在的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代。

3. 标记整理算法

针对老年代对象的存亡特征,1974年Edward Lueders提出了另外一种有针对性的“标记-整理”算法,其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,像这样的停顿被最初的虚拟机设计者形象地描述为“Stop The World”。

14、说说七个垃圾回收器?

串行收集器Serial

Serial收集器是最基础、历史最悠久的收集器,使用复制算法,曾经是HotSpot虚拟机新生代收集器的唯一选择。这个收集器是一个单线程工作的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。也就是说它在进行垃圾收集时,会发生“Stop The World”

老年代串行收集器Serial Old
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法,这个收集器的主要意义也是供客户端模式下的HotSpot虚拟机使用。

ParNew收集器

ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致,在实现上这两种收集器也共用了相当多的代码。

Parallel Scavenge收集器
Parallel Scavenge收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。

Parallel Old

Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的搭配组合,在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合。

CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,从名字上就可以看出CMS收集器是基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:初始标记、并发标记、重新标记、并发清除。

G1
Garbage First(简称G1)收集器是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。到了JDK 8 Update 40的时候,G1提供并发的类卸载的支持,补全了其计划功能的最后一块拼图。这个版本以后的G1收集器才被Oracle官方称为“全功能的垃圾收集器”。

15、请你讲下CMS(并发标记清除)回收器?

CMS收集器是一种以获取最短回收停顿时间为目标的收集器,从名字上就可以看出CMS收集器是基于标记清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:初始标记、并发标记、重新标记、并发清除。其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。

STW:Stop-The-World是在垃圾回收算法执行过程中,将jvm内存冻结,停顿的一种状态。即暂停用户线程。

1. 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。

2. 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。

3. 重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。

4. 并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。 指定老年代使用CMS GC: -XX:+UseConcMarkSweepGC

16、请你讲下G1垃圾优先回收器?

G1也仍是遵循分代收集理论设计的,但其堆内存的布局与其他收集器有非常明显的差异:G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。 巨大区:此外,还有一类专门用来存储大对象的特殊区域(Humongous Region)。G1认为只要超过了Region一半的对象即可判定为大对象。而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中,G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待。 更具体的处理思路是,让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小(垃圾数量),价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间,优先处理回收价值收益最大的那些Region,这也就是“Garbage First”名字的由来。 G1收集器的运作过程大致可划分为以下四个步骤:初始标记、并发标记、最终标记、筛选回收。其中,初始标记和最终标记阶段仍然需要停顿所有的线程,但是耗时很短。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值