JVM调优必备理论知识

对象从出生到消亡过程

这也是jvm堆内存结构的详情:参考jvm内存区域

新生代概念(三分之一)

新生代分为一个eden区和两个survivor区,默认的比例是8:1:1

eden区是我们new出来对象之后往里面扔的那块区,回收一次跑到survivor

新生代大量死去少量存活 采用复制算法

思考:为什么新生代采用复制算法?

回答:复制算法是将内存按容量划分大小相等的两块,每次只使用一块。当这一块的内存用完了,就将还存活的对象复制到另外一块内存上面。

新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,只需要对少量存活的对象复制就可以进行收集

老年代概念(三分之二)

老年代存活率高回收较少 采用MC或MS

YGC:年轻代耗尽时触发

FullGC:在老年代无法继续分配空间时触发,新生代老年代同时进行回收,所谓JVM调优就是尽量减少FGC

一个对象从出生到消亡

一个对象产生之后首先在栈上分配,栈上分配不下就会进入TLAB  tlab分配不下了就会进eden区

eden经过一次垃圾回收之后,进入survivor区(s1),

s1在经过一次垃圾回收之后进入s2

此后在s1和s2游走,

什么时候年龄够了 进入老年代

栈上分配:(栈上分配不需要垃圾回收,直接弹出就没了)

  -线程私有小对象

  -无逃逸

  -支持标量替换

  -无需调整

线程本地分配(Thread Local Allocation Buffer):(不需要和其它线程去争用)

  -占用eden 默认1%(每个线程在eden区取1%空间)

  -多线程的时候 不用竞争eden就可以申请空间 提高效率

  -小对象

  -无需调整

老年代:

  -大对象(大对象直接进入老年代)

补充:动态年龄

 

常见的垃圾回收器

1.JDK诞生 Serial追随 提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,

它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW

2.Serial 年轻代 串行回收(a stop-the-word,copying collector which uses a single gc thread) STW时间非常长
3.PS Parallel Scavenge  年轻代 并行回收(a stop-the-word copying collector which uses multiple GC threads)
4.ParNew 年轻代 配合CMS的并行回收(from parallel Scavenge in that is has enhancements that make it usable with CMS)
5.SerialOld (a stop-the-word,mark-sweep-compact collector that uses a single GC thread)
6.ParallelOld(a compacting collector that uses multiple GC threads)
7.ConcurrentMarkSweep 老年代 并发的(FGC) 垃圾回收和应用程序同时运行,降低STW的时间(10g内存十几秒—>200ms) CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定。

CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收.

解决方案:保持老年代有足够的空间 -XX:CMSInitiatingOccupancyFraction 92%(JDK1.7之后默认92)降低这个值:这个值的意思是CMS收集器当老年代使用了92%的时候会被激活。

        JDK1.5的默认设置下CMS收集器当老年代使用了68%后会被激活,这是一个偏保守的设置。适当的提高这个值可以降低内存回收次数从而获得更好的性能。

这个值的意思是CMS收集器当老年代使用了92%的时候会被激活。越大 回收次数越低,但是每次时间越长。越小 回收次数就越多,每次时间就越低

想象一下: PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW) 几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC 算法:三色标记 + Incremental Update

CMS常见的几个阶段:

—>初始标记(只标记最根上的对象STW)

—>并发标记(百分之八十时间)

—>重新标记(STW 并发标记过程中错标 重新标记) CMS和G1采用的三色标记,区别是CMS增量更新,G1采用的快照替换

—>并发清理(这个过程产生的垃圾叫浮动垃圾 只能等下一次CMS)

8.G1(10ms) 算法:三色标记 + SATB  G1的响应时间高于PN+CMS  但是吞吐量没有PN+CMS高(少百分之十)

垃圾收集器跟内存大小的关系
Serial 几十兆
PS 上百兆 - 几个G
CMS - 20G
G1 - 上百G
ZGC - 4T - 16T(JDK13)

区分概念:

内存泄漏memory leak:有一块内存被一个废了的对象占用 也不回收它,叫内存泄露,内存泄露不一定内存溢出

内存溢出out of memory:不断产生对象 ,内存撑不住了

吞吐量:用户代码时间 / (用户代码执行时间+垃圾回收时间) 吞吐量越大说明干正常事的时间越多。

响应时间:STW越短,响应时间越好

所谓调优首先确定追求啥?吞吐量优先还是响应时间优先?在满足一定响应时间的情况下,要求达到多大的吞吐量......

科学计算,数据挖掘,thrput 。吞吐量优先的一般:(PS+PO)

响应时间优先:网站 GUI API(1.8 G1 也可以ParNew+CMS)

什么是调优

1.根据需求进行JVM的规划和预调优

2.优化运行JVM运行环境

3.解决JVM运行过程中出现的各种问题(OOM)

调优步骤

2. java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.gc.T15_FullGC_Problem01

3. 一般是运维团队首先受到报警信息(CPU Memory)

4. top命令观察到问题:内存不断增长 CPU占用率居高不下

5.top -Hp pid可以查看某个进程的线程信息,-H 显示线程信息,-p指定pid

6. jstack 定位线程状况,jstack threadId > test.txt(linux命令行显示的线程id是十进制 dump下来的线程id是十六进制)

  重点关注:WAITING BLOCKED

eg. waiting on <0x0000000088ca3310> (a java.lang.Object) 假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁 怎么找搜索jstack dump的信息,找 ,看哪个线程持有这把锁RUNNABLE

7. 为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称 怎么样自定义线程池里的线程名称?(自定义ThreadFactory)

8. jinfo pid(把该进程详细的虚拟机信息列出来 启动参数等 )

9. jstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用) jstat -gc 4655 500 : 每个500个毫秒打印GC的情况 如果面试官问你是怎么定位OOM问题的?如果你回答用图形界面(错误)

  1:已经上线的系统不用图形界面用什么?(cmdline arthas)

  2:图形界面到底用在什么地方?测试!测试的时候进行监控!(压测观察)

10. jmap - histo 4655 | head -20,查找有多少对象产生

11. jmap -dump:format=b,file=xxx pid :

  线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)

  1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件

  2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响

  3:在线定位(一般小点儿公司用不到)

12. java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01

13. 使用MAT / jhat /jvisualvm 进行dump文件分析 https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html jhat -J-mx512M xxx.dump http://192.168.17.11:7000 拉到最后:找到对应链接 可以使用OQL查找特定问题对象

找到代码的问题

G1算法

以前在物理上是两块连续的内存,一块年轻代 一块老年代,空间太大,无论怎么样也提升不了多少了。

然后利用了分而治之的思想,现在数据量越来越大,分而治之的思想越来越重要

分而治之和分层是架构设计的两大思想。

G1把内存分层了一个个的Region (1 2 4...32)

每个Region在逻辑上依然属于某一个分代,old区 survioe区 Eden区 Humongous区(大对象区)

每次回收 垃圾最多的那些个Region 也就是G1的名字由来(garbage first),

因为G1产生FGC 和CMS一样也是用的serialOld(JDK10以后会用并行的)

G1和CMS调优目标一样,尽量别有FGC

可以降低降低MixedGC触发值,让MixedGC提早发生(FGC的频率会变低)

  XX:InitiatingHeapOccupancyPercent - 启动并发GC时的堆内存占用百分比.

    默认值45% 

MixedGC相当于一个完整的CMS

G1的特点

  并发收集

  压缩空闲空间不会延长GC的暂停时间

  更容易预测GC暂停时间    

    G1还有一个特点,新生代和老年代的比例是动态的,不需要手动指定,因为这是G1预测停顿时间的基准5%-60%

    如果STW的时间比较长,G1就会把Y区稍微调小一点,不用机器停掉 手工去调

控制可预测GC的暂停时间。

  适用不需要实现很高吞吐量的场景,但需要很高响应时间

转载GC调优注意点:

G1调优注意点

Mixed GC 期间老年代空间被填满(晋升失败)

类似 CMS,常见的解决是:

减小 -XX:InitiatingHeapOccupancyPercent 提前启动标记周期。

Mixed GC:当老年代空间达到阈值会触发 Mixed GC,选定所有新生代里的 Region,

根据全局并发标记阶段(下面介绍到)统计得出收集收益高的若干老年代 Region

最后简单归纳一下:

G1把内存分成一块块的Region,每块的Region的大小都是一样的。
G1保留了YGC并加上了一种全新的MIXGC用于收集老年代。G1中没有Full GC,G1中的Full GC是采用serial old Full GC。在MIXGC中的Cset是选定所有young gen里的region,外加根据global concurrent marking统计得出收集收益高的若干old gen region。在YGC中的Cset是选定所有young gen里的region。通过控制young gen的region个数来控制young GC的开销。YGC与MIXGC都是采用多线程复制清除,整个过程会STW。
G1的低延迟原理在于其回收的区域变得精确并且范围变小了。
全局并发标记分的五个阶段。
用STAB来维持并发GC的准确性。
使用G1的最佳实践
G1 GC日志打印

https://zhuanlan.zhihu.com/p/52841787

posted @ 2020-06-09 00:16  palapala  阅读(406)  评论(0编辑  收藏  举报