目录:
- [第一题 你们项⽬如何排查JVM问题](#第一题 你们项⽬如何排查JVM问题)
- [第二题 ⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?](#第二题 ⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?)
- [第三题 怎么确定⼀个对象到底是不是垃圾?](#第三题 怎么确定⼀个对象到底是不是垃圾?)
- [第四题 JVM有哪些垃圾回收算法?](#第四题 JVM有哪些垃圾回收算法?)
- [第五题 什么是STW?](#第五题 什么是STW?)
第一题 你们项⽬如何排查JVM问题
对于还在正常运⾏的系统:
- 可以使⽤jmap来查看JVM中各个区域的使⽤情况
- 可以通过jstack来查看线程的运⾏情况,⽐如哪些线程阻塞、是否出现了死锁
- 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc⽐较频繁,那么就得进⾏调优了
- 通过各个命令的结果,或者jvisualvm等⼯具来进⾏分析
- ⾸先,初步猜测频繁发送fullgc的原因,如果频繁发⽣fullgc但是⼜⼀直没有出现内存溢出,那么表示fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这些对象进⼊到⽼年代,对于这种情况,就要考虑这些存活时间不⻓的对象是不是⽐较⼤,导致年轻代放不下,直接进⼊到了⽼年代,尝试加⼤年轻代的⼤⼩,如果改完之后,fullgc减少,则证明修改有效
- 同时,还可以找到占⽤CPU最多的线程,定位到具体的⽅法,优化这个⽅法的执⾏,看是否能避免某些对象的创建,从⽽节省内存
对于已经发⽣了OOM的系统:
-
⼀般⽣产系统中都会设置当系统发⽣了OOM时,⽣成当时的dump⽂件(-
XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)
-
我们可以利⽤jsisualvm等⼯具来分析dump⽂件
-
根据dump⽂件找到异常的实例对象,和异常的线程(占⽤CPU⾼),定位到具体的代码
-
然后再进⾏详细的分析和调试
第二题 ⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?
- ⽤户创建⼀个对象,JVM⾸先需要到⽅法区去找对象的类型信息。然后再创建对象。
- JVM要实例化⼀个对象,⾸先要在堆当中先创建⼀个对象。-> 半初始化状态
- 对象⾸先会分配在堆内存中新⽣代的Eden。然后经过⼀次Minor GC,对象如果存活,就会进⼊S区。在后续的每次GC中,如果对象⼀直存活,就会在S区来回拷⻉,每移动⼀次,年龄加1。-> 多⼤年龄才会移⼊⽼年代? 年龄最⼤15, 超过⼀定年龄后,对象转⼊⽼年代。
- 当⽅法执⾏结束后,栈中的指针会先移除掉。
- 堆中的对象,经过Full GC,就会被标记为垃圾,然后被GC线程清理掉。
第三题 怎么确定⼀个对象到底是不是垃圾?
- 引⽤计数: 这种⽅式是给堆内存当中的每个对象记录⼀个引⽤个数。引⽤个数为0的就认为是垃圾。这是早期JDK中使⽤的⽅式。引⽤计数⽆法解决循环引⽤的问题。
- 根可达算法: 这种⽅式是在内存中,从引⽤根对象向下⼀直找引⽤,找不到的对象就是垃圾。
第四题 JVM有哪些垃圾回收算法?
- MarkSweep 标记清除算法:这个算法分为两个阶段,标记阶段:把垃圾内存标记出来,清除阶段:直接将垃圾内存回收。这种算法是⽐较简单的,但是有个很严重的问题,就是会产⽣⼤量的内
存碎⽚。 - Copying 拷⻉算法:为了解决标记清除算法的内存碎⽚问题,就产⽣了拷⻉算法。拷⻉算法将内存分为⼤⼩相等的两半,每次只使⽤其中⼀半。垃圾回收时,将当前这⼀块的存活对象全部拷⻉到另⼀半,然后当前这⼀半内存就可以直接清除。这种算法没有内存碎⽚,但是他的问题就在于浪费空间。⽽且,他的效率跟存货对象的个数有关。
- MarkCompack 标记压缩算法:为了解决拷⻉算法的缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清除算法是⼀样的,但是在完成标记之后,不是直接清理垃圾内存,⽽是将存活对象往⼀端移动,然后将端边界以外的所有内存直接清除。
第五题 什么是STW?
STW: Stop-The-World,是在垃圾回收算法执⾏过程当中,需要将JVM内存冻结的⼀种状态。在STW状态下,JAVA的所有线程都是停⽌执⾏的-GC线程除外,native⽅法可以执⾏,但是,不能与JVM交互。GC各种算法优化的重点,就是减少STW,同时这也是JVM调优的重点
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!