0.前言  建议与我的八股专栏的jvm八股配合使用。觉得本贴有用的收藏点个赞评论下送朵花什么的吧。下面我会让你看看什么是传说中的jvm调优(八股面试吹b版)。  打个小广告  我的架构设计专栏:https://www.nowcoder.com/creation/manager/columnDetail/0ybvLm  我的八股专栏:https://www.nowcoder.com/creation/manager/columnDetail/j8ZZk0  内有详细苍穹外卖话术哦!  为什么我要开个八股专栏?  一方面有感于现在面试找工作对八股的要求之高,一方面有感于市面上的一些八股资料又乱又复杂难懂,所以我决定自己结合各种市面上优秀的博客、gpt,牛客上的面经和我自己的理解,整合出一份八股资料,我的目标是:让八股文成为真正简单易懂的八股,知识点丰富且浅显易懂,不追求过分的深入,但一定重点层次分明(如果想深入了解知识点的话还是建议自己认真看看javaguide和小林coding,但其实我就是看了他们的全部博客取精华精炼部分融入我的专栏。。。),一句话就是我的八股专栏主打一个面试速成,一点超纲的知识点都不会有,因为我自己也用这份笔记准备秋招。。。  1.调优原则  JVM调优应该是Java性能优化的最后一颗子弹。性能问题一般第一选择是优化程序,最后的选择才是进行JVM调优。    2.JVM调优时机  不得不考虑进行JVM调优的是那些情况呢?     Heap内存(老年代)持续上涨达到设置的最大内存值;   Full GC 次数频繁;   GC 停顿时间过长(超过1秒);   应用出现OutOfMemory 等内存异常;   应用中有使用本地缓存且占用大量内存空间;   系统吞吐量与响应性能不高或下降    3.JVM调优目标     减少STW,提升用户体验   降低内存占用        吞吐量、延迟、内存占用三者类似CAP,构成了一个不可能三角,只能选择其中两个进行调优,不可三者兼得。     延迟:GC低停顿和GC低频率;   低内存占用;   高吞吐量;    选择了其中两个,必然会会以牺牲另一个为代价。  下面展示了一些JVM调优的量化目标参考实例:     Heap 内存使用率 <= 70%;   Old generation内存使用率<= 70%;   avgpause <= 1秒;   Full gc 次数0 或 avg pause interval >= 24小时 ;    注意:不同应用的JVM调优量化目标是不一样的。  4.JVM调优的步骤  一般情况下,JVM调优可通过以下步骤进行:     分析系统系统运行情况:分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;   确定JVM调优量化目标;   确定JVM调优参数(根据历史JVM参数来调整);   依次确定调优内存、延迟、吞吐量等指标;   对比观察调优前后的差异;   不断的分析和调整,直到找到合适的JVM参数配置;   找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。      jvm常用命令    top命令找到消耗 CPU 很高的进程 id  top命令用于实时监控系统进程和资源占用情况。    PID  进程号USER  用户名PR  优先级NI  Nice值VIRT  虚拟内存RES  常驻内存SHR  共享内存S  当前状态%CPU  占用CPU百分比%MEM  占用内存百分比TIME+  运行时长COMMAND  启动命令  top -p 4337 显示目标进程信息    然后在该界面输入 H,则展示该进程下所有的线程信息,如下:    jps查看java进程  输出JVM中运行的进程状态信息  jps -l :查看所有进程号;  jstack对当前的进程做 dump,输出所有的线程信息  查看java进程内线程的堆栈信息。  jstack [option] <pid>    使用jstack查看进行堆栈运行信息    可以看到它会输出所有线程的堆栈信息,并且存在一些 16 进制的 nid。  把上面得到的cpu利用率恶高的线程 id 4340,4339进行 16 进制转换:   4340---10F4 4339---10F3  把转换的进制在第 4 步的线程信息中查找,如下:    从图中可以看出,造成 CPU100% 的原因是在由于垃圾回收线程导致的,而可能不是我们业务线程导致的。  jinfo查看jvm参数  jinfo -flags [port]    jstat 查看 GC 情况  主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。  是JVM统计监测工具。可以用来显示垃圾回收信息、类加载信息、新生代统计信息等。  jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。  常见参数:  ①总结垃圾回收统计  jstat -gcutil pid                              字段     含义             S0     幸存1区当前使用比例             S1     幸存2区当前使用比例             E     伊甸园区使用比例             O     老年代使用比例             M     元数据区使用比例             CCS     压缩使用比例             YGC     年轻代垃圾回收次数             YGCT     年轻代垃圾回收消耗时间             FGC     老年代垃圾回收次数             FGCT     老年代垃圾回收消耗时间             GCT     垃圾回收消耗总时间           ②垃圾回收统计  //每间隔 2s 总共查询 10 次 GC 情况jstat -gc 4337 2000 10    S0C:第一个幸存区(From 区)的大小S1C:第二个幸存区(To 区)的大小S0U:第一个幸存区的使用大小S1U:第二个幸存区的使用大小EC:伊甸园(Eden)区的大小EU:伊甸园(Eden)区的使用大小OC:老年代大小OU:老年代使用大小MC:方法区大小MU:方法区使用大小CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数YGCT:年轻代垃圾回收消耗时间FGC:老年代垃圾回收次数FGCT:老年代垃圾回收消耗时间GCT:垃圾回收消耗总时间  从上图可以看出,YoungGC 只进行了 19 次,而进行了大量的 Full GC(7000多次且继续递增),同时PrintGC还显示抛出了 OutOfMemory。    结论:这里的 CPU 占用过高不是业务线程导致,而是 GC 线程占用过高导致的,JVM 在疯狂的进行垃圾回收,JVM 中默认的垃圾回收器是多线程的,所以多线程在疯狂回收,导致 CPU 占用过高。  jmap生成堆转储快照,执行一下命令打印堆内存相关信息    它为什么会进行疯狂的垃圾回收?很显然,内存不够,我们通过jmap看一下堆的内存情况,如下:  jmap –heap 4337    很明显的可以看到,老年代已经被塞得满满的了,也就是说 GC 好像没有作用了,有人说为什么没有自动扩容(代码中有注释,设置了 -Xmx=200M,目的是快速测试),就算没有设置,随着时间最后也会塞满。  因此,问题的根本很可能发生了内存泄漏。  我们可以通过 jmap导出堆的快照(即 dump 文件),但是导出 dump 文件过大会对程序造成影响,因此我们可以先jmap -histo pid,它可以打印每个 class 的实例数目,内存占用,类全名信息。如下:  //把JVM中的对象全部打印出来, 但是这样太多了,那么我们选择前 20 的对象展示出来jmap –histo 4337 | head -20    一般来说,前面这几行,就可以看出,到底是哪些对象占用了内存。我们知道对象都是朝生夕死的,那么为什么这些对象不死呢?回收不掉呢?其结果导致了 FullGC 和 OutOfMemory。  我们再回顾上面的代码,我们把线程池的最大线程数设置为 50,而我们的任务数为 100,任务数多于线程数,那么任务会进入阻塞队列,由于任务数一直多于线程数,所以每 0.1s 就会有 50 个任务进入阻塞队列,没有执行,但同时这些对象又被线程池对象 Executor 引用,且Executor 是一个 GCroots,所以堆中有 60 万个对象(UserInfo),阻塞队列中有 60 万个任务(ScheduledFutureTask),并且这些对象还无法回收。  因此,我们可以定位到代码,要么加大线程池的线程数,要么减少批量任务数。    jvm调优的策略  选择合适的垃圾回收器     CPU 单核:那么  
点赞 11
评论 3
全部评论

相关推荐

03-03 19:02
已编辑
东华理工大学 Node.js
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务