Java 虚拟机(JVM):JVM是一个抽象的计算机结构。Java程序根据JVM的特性编写。JVM针对特定于操作系统并且可以将Java指令翻译成底层系统的指令并执行。JVM确保了Java的平台无关性。
1. Java内存区域的划分
程序计数器
虚拟机栈
本地方法栈
java堆
方法区
前三个是线程私有的,后两个是线程共享的。
字节码解释器通过改变程序计数器的值来决定下一条要执行的指令,为了在线程切换后每条线程都能正确回到上次执行的位置,因为每条线程都有自己的程序计数器。
虚拟机栈是存放Java方法内存模型,每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法返回地址等信息。方法的开始调用对应着栈帧的进栈,方法执行完成对应这栈帧的出栈。位于栈顶被称为“当前方法”。
本地方法栈和虚拟机栈类似,不过虚拟机栈针对Java方法,而本地方法栈针对Native方法。
Java堆。对象实例被分配内存的地方,也是垃圾回收的主要区域。
方法区。存放被虚拟机加载的类信息、常量(final)、静态变量(static)、即时编译期编译后的代码。方法区是用永久代实现的,这个区域的内存回收目标主要是针对常量池的回收和类型的卸载。运行时常量池是方法区的一部分,运行时常量池是Class文件中的一项信息,存放编译期生成的各种字面量和符号引用。
2. 新生代和老年代,对象如何进入老年代,新生代如何变成老年代?
Java堆分为新生代和老年代。在新生代又被划分为Eden区,From Sruvivor和To Survivor区,比例是8:1:1,所以新生代可用空间其实只有其容量的90%。对象优先被分配在Eden区。
不过大对象比如长字符串、数组由于需要大量连续的内存空间,所以直接进入老年代。这是对象进入老年代的一种方式,
还有就是长期存活的对象会进入老年代。在Eden区出生的对象经过一次Minor GC会若存活,且Survivor区容纳得下,就会进入Survivor区且对象年龄加1,当对象年龄达到一定的值,就会进入老年代。
在上述情况中,若Survivor区不能容纳存活的对象,则会通过分配担保机制转移到老年代。
同年龄的对象达到suivivor空间的一半,大于等于该年龄的对象会直接进入老年代。
3. 说下垃圾回收的流程,然后针对性地说下如何在代码中优化内存性能
垃圾收集GC(Garbage Collection)是Java语言的核心技术之一,垃圾回收,回收的垃圾是什么?
由此可见,在运行时,Java的实例被存放在堆内存区域。当一个对象不再被引用时,满足条件就会从堆内存移除。在垃圾回收进程中,这些对象将会从堆内存移除并且内存空间被回收。超出了作用域或引用计数为空的对象;从gc root开始搜索找不到的对象,而且经过一次标记、清理,仍然没有复活的对象。
GC是怎么工作的?作为一个自动的过程,程序员不需要在代码中显示地启动垃圾回收过程,一个对象实例化时,大多数情况下,直接在 Eden 区中进行分配,如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC;如果内存还是不足,那么就会进行一次 Full GC。
Java堆分为新生代和老年代,采用了不同的回收方式。
发生在新生代的GC称为Minor GC,当Eden区被占满了而又需要分配内存时,会发生一次Minor GC,一般使用复制算法,将Eden和From Survivor区中还存活的对象一起复制到To Survivor区中,然后一次性清理掉Eden和From Survivor中的内存,使用复制算法不会产生碎片。
老年代的GC称为Full GC或者Major GC:
当老年代的内存占满而又需要分配内存时,会发起Full GC
调用System.gc()时,可能会发生Full GC,并不保证一定会执行。
在Minor GC后survivor区放不下,通过担保机制进入老年代的对象比老年代的内存空间还大,会发生Full GC;
在发生Minor GC之前,会先比较历次晋升到老年代的对象平均年龄,如果大于老年代的内存,也会触发Full GC。如果不允许担保失败,直接Full GC。
4. 对象在什么时候可以被回收,调用finalize方法后一直会被回收吗
在经过可达性分析后,到GC Roots不可达的对象可以被回收(但并不是一定会被回收,至少要经过两次标记),此时对象被第一次标记,并进行一次判断:
如果该对象没有调用过或者没有重写finalize()方法,那么在第二次标记后可以被回收了;
否则,该对象会进入一个FQueue中,稍后由JVM建立的一个Finalizer线程中去执行回收,此时若对象中finalize中“自救”,即和引用链上的任意一个对象建立引用关系,到GC Roots又可达了,在第二次标记时它会被移除“即将回收”的集合;如果finalize中没有逃脱,那就面临被回收。
因此finalize方法被调用后,对象不一定会被回收。
5. 如果出现了OOM异常,该怎么排查?如何看Dump文件。
6. Java的异常处理机制。
在整个java的异常处理中,实际上也是按照面向对象的方式进行处理,处理的步骤如下:
一旦产生异常,则首先会产生一个异常类的实例化对象;
在try语句中对此异常对象进行捕捉;
产生异常的对象与catch语句中的各个异常类型进行匹配,如果匹配成功,则执行catch语句中的代码。
下一篇,我们将从框架方面整理面试的知识点.
7. 垃圾回收算法有哪些
复制算法,一般用于新生代的垃圾回收
标记清除CMS, 一般用于老年代的垃圾回收
标记整理G1,一般用于老年代的垃圾回收
分代收集:根据对象存活周期的不同把Java堆分为新生代和老年代。新生代中又分为Eden区、from survivor区和to survivor区,默认8:1:1,对象默认创建在Eden区,每次垃圾收集时新生代都会有大量对象死亡。此时利用复制算法将Eden区和from survivor区还存活的对象一并复制到tosurvivor区。老年代的对象存活率高,没有额外空间进行分配担保,因此采用标记-清除或者标记-整理的算法进行回收。前者会产生空间碎片,而后者不会。
8.说一说CMS和G1垃圾收集器?各有什么特点
CMS(Concurrent Mark Sweep) 从名字可以看出是可以进行并发标记-清除的垃圾收集器。针对老年代的垃圾收集器,目的是尽可能地减少用户线程的停顿时间。
收集过程有如下几个步骤:
初始标记:标记从GC Roots能直接关联到的对象,会暂停用户线程
并发标记:即在堆中堆对象进行可达性分析,从GC Roots开始找出存活的对象,可以和用户线程一起进行
重新标记:修正并发标记期间因用户程序继续运作导致标记产生变动的对象的标记记录
并发清除:并发清除标记阶段中确定为不可达的对象
CMS的缺点:
由于是基于标记-清除算法,所以会产生空间碎片
无法处理浮动垃圾,即在清理期间由于用户线程还在运行,还会持续产生垃圾,而这部分垃圾还没有被标记,在本次无法进行回收。
对CPU资源敏感
CMS比较类似适合用户交互的场景,可以获得较小的响应时间。
G1(Garbage First),有如下特点:
并行与并发
分代收集
空间整合 :整体上看是“标记-整理”算法,局部(两个Region之间 )看是复制算法。确保其不会产生空间碎片。(这是和CMS的区别之一)
可预测的停顿:G1除了追求低停顿外,还能建立可预测的时间模型,主要原因是它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。
在使用G1收集器时,Java堆的内存划分为多个大小相等的独立区域,新生代和老年代不再是物理隔离。G1跟踪各个区域的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的区域。
G1的收集过程和CMS有些类似:
初始标记:标记与GC Roots直接关联的对象,会暂停用户线程(Stop the World)
并发标记:并发从GC Roots开始找出存活的对象,可以和用户线程一起进行
最终标记:修正并发标记期间因用户程序继续运作导致标记产生变动的对象的标记记录
筛选回收:清除标记阶段中确定为不可达的对象,具体来说对各个区域的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。
G1的优势:可预测的停顿;实时性较强,大幅减少了长时间的gc;一定程度的高吞吐量。
9.CMS和G1的区别
由上一个问题可总结出CMS和G1的区别:
G1堆的内存布局和其他垃圾收集器不同,它将整个Java堆划分成多个大小相等的独立区域(Region)。G1依然保留了分代收集,但是新生代和老年代不再是物理隔离的,它们都属于一部分Region的集合,因此仅使用G1就可以管理整个堆。
CMS基于标记-清除,会产生空间碎片;G1从整体看是标记-整理,从局部(两个Region之间)看是复制算法,不会产生空间碎片。
G1能实现可预测的停顿。
(完) | Web小项目
作者:junxia
如果您认为阅读这篇博客让您有些收获,欢迎扫描下方二维码关注我们,与我们交流互动。
本公众号专注Java面试题目的收集和解析以及【Web项目】源码的分享,。
以下是公众号截图,欢迎小伙伴的鼓励!