jvm 相关问题记录

由 zrc 发布

内存分配

堆里面分为新生代和老生代(java8取消了永久代,采用了Metaspace),新生代包含Eden+Survivor区,survivor区里面分为from和to区,内存回收时,如果用的是复制算法,从from复制到to,当经过一次或者多次GC之后,存活下来的对象会被移动到老年区,当JVM内存不够用的时候,会触发Full GC,清理JVM老年区

当新生区满了之后会触发YGC,先把存活的对象放到其中一个Survice
区,然后进行垃圾清理。因为如果仅仅清理需要删除的对象,这样会导致内存碎
片,因此一般会把Eden 进行完全的清理,然后整理内存。那么下次GC 的时候,
就会使用下一个Survive,这样循环使用。如果有特别大的对象,新生代放不下,
就会使用老年代的担保,直接放到老年代里面。因为JVM 认为,一般大对象的存
活时间一般比较久远。

java堆内存分配比例:

a156ac14.png

6b4c25d4.png

现网业务中对jvm 的设置

-server -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:InitialRAMFraction=2 -XX:MaxRAMFraction=2 -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

NewRatio:
老年代与新生代的占比 即: newRatio: 1 其中1为新生代
例如 newRatio = 1 ,那么老年代和新生代的比例为 1:1 ,那么老年代占用整个堆的1/2

SurvivorRatio:
新生代中,Eden和Survivor 的比例,由于survivor 有两个区域,认为 s0 和s1 的比例固定为1
即SurvivorRatio:1:1 为 设置为Eden 的比例
SurvivorRatio:1:1 为 Eden: s0:s1
例如 SurviorRatio为8 那么 Eden:s0:s1 为 8:1:1 (默认值)

cpu 问题定位:

2.2 定位具体的异常业务

这里咱们可以使用 pwdx 命令根据 pid 找到业务进程路径,进而定位到负责人和项目:

可得出结论:该进程对应的就是数据平台的web服务。

2.3 定位异常线程及具体代码行

传统的方案一般是4步:

1、top oder by with P:1040 // 首先按进程负载排序找到 maxLoad(pid)
2、top -Hp 进程PID:1073 // 找到相关负载 线程PID
3、printf “0x%x\n”线程PID: 0x431 // 将线程PID转换为 16进制,为后面查找 jstack 日志做准备
4、jstack 进程PID | vim +/十六进制线程PID - // 例如:jstack 1040|vim +/0x431 -

但是对于线上问题定位来说,分秒必争,上面的 4 步还是太繁琐耗时了,之前介绍过淘宝的oldratlee 同学就将上面的流程封装为了一个工具:show-busy-java-threads.sh,可以很方便的定位线上的这类问题:


暂无评论

发表评论