脚本宝典收集整理的这篇文章主要介绍了【面试题】JVM常见面试题(二),脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
主要由 4 个部分组成:
class
字节码文件中的指令。java.lang.Object
)来装载 .class
文件到运行时数据区中的方法区中。① 物理地址方面的区别:
② 内存分配方面的区别:
运行期
确认的,因此大小不固定。一般堆大小远远大于虚拟机栈。编译期
就确认,大小是固定的。③ 存放的内容方面的区别:
注:静态变量放在方法区,而静态的对象还是放在堆。
④ 线程共享方面的区别:
可以从这几个方面考虑:
定义:标记清除算法顾名思义,是指在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后垃圾收集器根据标识清除相应的内容,给堆内存腾出相应的空间
缺点:容易产生大量的内存碎片,可能无法满足大对象的内存分配,一旦导致无法分配对象,那就会导致jvm启动gc,一旦启动gc,我们的应用程序就会暂停,这就导致应用的响应速度变慢
标记 - 整理算法在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后整理剩余的对象,将可用的对象移动到一起,使内存更加紧凑,连续的空间就更多。可以有效避免因内存碎片而导致的问题,但是因为整体需要消耗一定的时间,所以效率较低
将内存分为等大小的两个区域,FROM和TO(TO中为空)。先将被GC Root引用的对象从FROM放入TO中,再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。
分代收集算法:这种算法是把 Java 堆分为新生代和老年代,新生代默认的空间占比总空间的1/3,老生代的默认占比是2/3
新生代里有 3 个分区:伊甸园、To 幸存区、From 幸存区,它们的默认占比是 8:1:1
。即-XX:SurvivorRatio=8
,其中Survivor分为From Survivor和ToSurvivor,因此Eden此时占新生代空间的80%
。
新创建的对象都被放在了新生代的伊甸园中
当伊甸园中的内存不足时,就会进行一次垃圾回收,这时的回收叫做 Minor GCMinor GC 会将伊甸园和幸存区FROM存活的对象先复制到 幸存区 TO中, 并让其寿命加1,再交换两个幸存区
再次创建对象,若新生代的伊甸园又满了,则会再次触发 Minor GC(会触发 stop the world, 暂停其他用户线程,只让垃圾回收线程工作),这时不仅会回收伊甸园中的垃圾,还会回收幸存区中的垃圾,再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区,并让幸存区中的对象寿命加1
如果幸存区中的对象的寿命超过某个阈值(最大为15,4bit),就会被放入老年代中
如果新生代老年代中的内存都满了,就会先触发Minor GC,再触发Full GC,扫描新生代和老年代中所有不再使用的对象并回收
当遇到一个较大的对象时,就算新生代的伊甸园为空,也无法容纳该对象时,会将该对象直接晋升为老年代
某个线程的内存溢出了而抛异常(out of memory),不会让其他的线程结束运行
这是因为当一个线程抛出OOM异常后,它所占据的内存资源会全部被释放掉,从而不会影响其他线程的运行,进程依然正常
老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法
+1
,并且交换 FROM 和 TO。15
时,会晋升至老年代。Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的老年代收集器
特点:基于标记-清除算法实现。并发收集、低停顿
应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务
CMS收集器的运行过程分为下列4步:
初始标记:标记GC Roots能直接到的对象。速度很快但是仍存在Stop The World问题
并发标记:进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行,不需要停顿
重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题
并发清除:对标记的对象进行清除回收,不需要停顿
主要缺点有:吞吐量低,导致 CPU 利用率不够高、无法处理浮动垃圾、它使用的回收算法“标记-清除”算法会导致收集结束时会有大量空间碎片产生
CMS的问题
并发回收导致cpu资源紧张
在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程而导致应用程序变慢,降低程序总吞吐量。CMS默认启动的回收线程数是:(CPU核数+3)/4,当CPU核数不足四个时,CMS对用户程序的影响就可能变得很大。
无法清理浮动垃圾:
在CMS的并发标记和并发清理阶段,用户线程还在继续运行,就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留到下一次垃圾收集时再清理掉。这一部分垃圾称为"浮动垃圾”。
并发失败(Concurrent Mode Failure):
由于在垃圾回收阶段用户线程还在并发运行,那就还需要预留足够的内存空间提供给用户线程使用,因此CMS不能像其他回收器那样等到老年代几乎完全被填满了再进行回收,必须预留一部分空间供并发回收时的程序运行使用。默认情况下,当老年代使用了92%的空间后就会触发CMS垃圾回收,这个值可以通过-XX:CMSInitiatingOccupancyFraction
参数来设置。
这里会有一个风险:要是CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次"并发失败”(Concurrent Mode Failure),这时候虚拟机将不得不启动后备预案:Stop The World,临时启用SerialOld来重新进行老年代的垃圾回收,这样一来停顿时间就很长了。
内存碎片问题:
CMS是一款基于"标记-清除”算法实现的回收器,这意味着回收结束时会有内存碎片产生。内存碎片过多时,将会给大对象分配带来麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次Full GC的情况。
为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection
开关参数(默认开启),用于在FullGC时开启内存碎片的合并整理过程,由于这个内存整理必须移动存活对象,是无法并发的,这样停顿时间就会变长。还有另外一个参数-XX:CMSFullGCsBeforeCompaction
,这个参数的作用是要求CMS在执行过若干次不整理空间的FullGC之后,下一次进入FullGC前会先进行碎片整理(默认值为0,表示每次进入FullGC时都进行碎片整理)。
Garbage First 收集器
JDK 9以后默认使用,而且替代了CMS 收集器
适用场景
相关参数:JDK8 并不是默认开启的,所需要参数开启
G1回收过程,G1回收器的运作过程大致可分为四个步骤:
初始标记(会STW):仅仅只是标记一下GCRoots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行MinorGC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
并发标记:从GCRoots开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理在并发时有引用变动的对象。
最终标记(会STW):对用户线程做短暂的暂停,处理并发阶段结束后仍有引用变动的对象。
清理阶段(会STW):更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,必须暂停用户线程,由多条回收器线程并行完成的。
调用 System.gc()
只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存
老年代空间不足
老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等
这种情况如何尽量避免?
应当尽量不要创建过大的对象以及数组。
可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。
还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间
空间分配担保失败
使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC
JDK 1.7 及以前的永久代空间不足 在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的 当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,此时也会触发Full GC
这种情况怎么避免?
为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC
Concurrent Mode Failure
执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC
内存泄漏(Memory Leak)是指本来无用的对象却继续占用内存,没有再恰当的时机释放占用的内存。 不使用的内存,却没有被释放,称为内存泄漏。 也就是该释放的没释放,该回收的没回收,比较典型的场景是: 每一个请求进来,或者每一次操作处理,都分配了内存,却有一部分不能回收(或未释放),那么随着处理的请求越来越多,内存泄漏也就越来越严重。
在Java中一般是指无用的对象却因为错误的引用关系,不能被GC回收清理。
static
成员变量,减少生命周期;以 JDK 8 为例:
名称 | 加载哪的类 | 说明 |
---|---|---|
Bootstrap ClassLoader(启动类加载器) | JAVA_HOME/jre/lib | 无法直接访问 |
Extension ClassLoader(扩展类加载器) | JAVA_HOME/jre/lib/ext | 上级为 Bootstrap,显示为 null |
Application ClassLoader(应用程序类加载器) | classpath | 上级为 Extension |
自定义类加载器 | 自定义 | 上级为 Application |
类加载器的优先级(由高到低):启动类加载器 -> 扩展类加载器 -> 应用程序类加载器 -> 自定义类加载器。
JAVA_HOME/jre/lib
目录中的,或者被-Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。用来加载java核心类库,无法被java程序直接引用JAVA_HOME/jre/lib/ext
目录中的,或者被 java.ext.dirs 系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。用来加载java的扩展库,java的虚拟机实现会提供一个扩展库目录,该类加载器在扩展库目录里面查找并加载java类;类加载的过程包括:加载、验证、准备、解析、初始化。其中验证、准备、解析统称为连接。
加载
、验证
、准备
和初始化
这四个阶段发生的顺序是确定的,而解析
阶段则不一定,它在某些情况下可以在初始化
阶段之后开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)
java.lang.Class
对象。static{}
)中的语句。如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
避免重复加载 + 避免核心类篡改
java.lang.Integer
的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的 java.lang.Integer
,而直接返回已加载过的 Integer.class
,这样便可以防止核心API库被随意篡改。自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法。
1 # JVM启动参数不换行
2 # 设置堆内存
3 ‐Xmx4g ‐Xms4g
4 # 指定GC算法
5 ‐XX:+UseG1GC ‐XX:MaxGCPauseMillis=50
6 # 指定GC并行线程数
7 ‐XX:ParallelGCThreads=4
8 # 打印GC日志
9 ‐XX:+PrintGCDetails ‐XX:+PrintGCDateStamps
10 # 指定GC日志文件
11 ‐Xloggc:gc.log
12 # 指定Meta区的最大值
13 ‐XX:MaxMetaspaceSize=2g
14 # 设置单个线程栈的大小
15 ‐Xss1m
16 # 指定堆内存溢出时自动进行Dump
17 ‐XX:+HeapDumpOnOutOfMemoryError
18 ‐XX:HeapDumpPath=/usr/local/
参考 https://csp1999.blog.csdn.net/article/details/117318685 https://blog.csdn.net/qq_45966440/article/details/121308864 https://blog.csdn.net/yanpenglei/article/details/119684369
以上是脚本宝典为你收集整理的【面试题】JVM常见面试题(二)全部内容,希望文章能够帮你解决【面试题】JVM常见面试题(二)所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。