查看原文
其他

小伙伴问题:Java堆内存用到100%了,咋办呀?该从哪里下手排查?

昨天有小伙伴问尼恩:
Java堆内存用到100%了,咋办呀?该从哪里下手排查?
尼恩提示:
按照下面的八股文,进行处理就可以了。
下面的八股文,既能帮大家 解决线上问题, 还能帮助大家 通过面试。 
可谓一箭双雕,一举两得,岂不美哉。

聊聊:堆内存设置的主要参数?

-Xms:初始堆大小(默认:物理内存的1/64)

-Xmx:最大堆大小(默认:物理内存的1/4)

-Xmn:新生代大小

-XX:SurvivorRatio:设置eden/form/ro的比例 (默认:8:1:1)

-XX:NewRatio:设置老年代/新生代的比例(默认:2)

-XX:+PrintGC/-XX:+PrintGCDetails :打印   GC   的过程信息

-XX:MaxTenuringThreshold:设置年轻代超过多少要进入老年代(默认:15)

聊聊:JVM运行的常用管理工具

  1. jps:查看当前系统运行的Java进程(进程号+名字;-m进程参数;-l程序的全路径;-v:传递给Java的main函数的函数参数)

  2. jstat:查看堆信息

  3. jinfo:查看虚拟机参数(也可以修改某些参数)

  4. jstack:查看线程的堆栈信息(查看线程拥有的锁,分析死锁的原因)

  5. jstatd:查看远程的Java进程

  6. jcmd:jdk7新增;可以查看Java进程,导出进程信息,执行GC等操作。

聊聊:JVM可视化管理工具(jdk自带的)有哪些?

  1. jconsole(jconsole)

  2. jvisualvm(visual VM)

  3. jmc(Mission Control)

查看堆内存,线程,加载类,cpu,dump等信息,检查死锁,内存溢出的原因

聊聊:哪些对象会被存放到老年代?

  1. 新生代对象每次经历⼀次minor gc,年龄会加1,当达到年龄阈值(默认为15岁)会直接进⼊老年代;

  2. 大对象直接进⼊老年代;

  3. 新生代复制算法需要⼀个survivor区进行轮换备份,如果出现大量对象在minor gc后仍然存活的情况时,就需要老年代进行分配担保,让survivor⽆法容纳的对象直接进⼊老年代;

  4. 如果在Survivor空间中相同年龄所有对象大⼩的总和大于Survivor空间的⼀半,年龄大于或等于该年龄的对象就可以直接进⼊年⽼代。

聊聊:什么时候触发full gc?

  1. 调用System.gc时,系统建议执行Full GC,但是不必然执行

  2. 老年代空间不⾜

  3. 方法区空间不⾜

  4. 通过Minor GC后进入老年代的平均大小大于老年代的可用内存

  5. 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

聊聊:七个垃圾回收器之间如何搭配使用

  1. Serial New收集器是针对新生代的收集器,采用的是复制算法;

  2. Parallel New(并行)收集器,新生代采用复制算法,老年代采用标记整理;

  3. Parallel Scavenge(并行)收集器,针对新生代,采用复制收集算法;

  4. Serial Old(串行)收集器,新生代采用复制,老年代采用标记清理;

  5. Parallel Old(并行)收集器,针对老年代,标记整理;

  6. CMS收集器,基于标记清理;

  7. G1收集器(JDK):“标记-复制”和“标记-整理”。从整体上看是基于“标记-整理”,从局部看,两个region之间是“标记-复制”。;

    综上:新生代基本采用复制算法,老年代采用标记整理算法。

聊聊:有没有JVM调优经验?JVM调优方案有哪些?

  1. 调优时机:

    a. heap 内存(老年代)持续上涨,达到设置的最大内存值;

    b. Full GC 次数频繁;

    c. GC 停顿时间过长(超过1秒);

    d. 应用出现OutOfMemory 等内存异常;

    e. 应用中有使用本地缓存,且占用大量内存空间;

    f. 系统吞吐量与响应性能不高或下降。

  2. 调优原则:

    a. 多数的Java应用不需要在服务器上进行JVM优化;

    b. 多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;

    c. 在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);

    d. 减少创建对象的数量;

    e. 减少使用全局变量和大对象;

    f. JVM优化,是到最后不得已才采用的⼿段;

    g. 在实际使用中,分析GC情况优化代码比优化JVM参数更好;

  3. 调优目标:

    a. GC低停顿;

    b. GC低频率;

    c. 低内存占用;

    d. 高吞吐量;

  4. 调优步骤:

    a. 分析GC日志及dump⽂件,判断是否需要优化,确定瓶颈问题点;

    b. 确定jvm调优量化目标;

    c. 确定jvm调优参数(根据历史jvm参数来调整);

    d. 调优⼀台服务器,对比观察调优前后的差异;

    e. 不断的分析和调整,知道找到合适的jvm参数配置;

    f. 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

聊聊:你们项目如何排查JVM问题的?

对于还在正常运行的系统:

  1. 可以使用jmap来查看JVM中各个区域的使用情况

  2. 可以通过jstack来查看线程的运行情况,比如哪些线程阻塞、 是否出现了死锁

  3. 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc比较频繁,那么就得进行调优了

  4. 通过各个命令的结果,或者jvisualvm等⼯具来进行分析

  5. 首先,初步猜测频繁发送fullgc的原因,如果频繁发⽣fullgc但是⼜⼀直没有出现内存溢出,那么表示 fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这些对象进⼊到老年代,对于这种情况,就要考虑这些存活时间不⻓的对象是不是比较大,导致年轻代放不下,直接进⼊到了老年代,尝试加大年轻代的大⼩,如果改完之后,fullgc减少,则证明修改有效

  6. 同时,还可以找到占用CPU最多的线程,定位到具体的方法,优化这个方法的执行,看是否能避免某些对象的创建,从而节省内存

对于已经发⽣了OOM的系统:

  1. ⼀般⽣产系统中都会设置当系统发⽣了OOM时,⽣成当时的dump⽂件(- XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)
  2. 我们可以利用jsisualvm等⼯具来分析dump⽂件
  3. 根据dump⽂件找到异常的实例对象,和异常的线程(占用CPU⾼),定位到具体的代码
  4. 然后再进行详细的分析和调试

总之,调优不是⼀蹴而就的,需要分析、 推理、 实践、 总结、 再分析,最终定位到具体的问题

聊聊:如何查看线程死锁?

  1. 可以通过jstack命令来进行查看,jstack命令中会显示发⽣了死锁的线程

  2. 或者两个线程去操作数据库时,数据库发⽣了死锁,这是可以查询数据库的死锁情况

1、 查询是否锁表
show OPEN TABLES where In_use > 0;
2、 查询进程
show processlist;
3、 查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
4、 查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

硬核面试题推荐


硬核文章推荐


硬核电子书

本文收录于:《尼恩Java 面试宝典》V19版


长按二维码,点击“识别图中二维码”即可查看老架构师尼恩个人微信,发暗号 “领电子书” 给尼恩,获取最新PDF。


  • 最新的《尼恩Java面试宝典》

    极致经典,不断升级,目前最新为V19


  • 尼恩Java高并发三部曲

    《Java高并发核心编程-卷1(加强版)》,不断升级

    《Java高并发核心编程-卷2(加强版)》,不断升级

    《Java高并发核心编程-卷3(加强版)》,不断升级


  • 尼恩架构笔记100篇+,不断添加 


继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存