美文网首页
jvm调优记录

jvm调优记录

作者: cutieagain | 来源:发表于2020-05-18 15:04 被阅读0次

    java虚拟机内存模型

    java虚拟机内存模型

    堆 heap

    • 堆是存放new Class()的对象,是gc的主要区域
    • 新生代划分为新生区和两个幸存区。
    • -xms是堆的初始容量,-xmx是堆的最大可拓展容量,建议初始值设置为最大值,避免反复拓展或者缩减的开销
    • -XX:NewRatio 是老年代 / 新生代的比例,默认值为2
    • -XX:SurvivorRatio 是新生区 / survivor#0的比例,survivor#0和survivor#1大小一致

    永久代 Permanent Generation

    • 存放类信息,常量,大对象,(比如 new byte[n] 对象)
    • -XX:PermSize 是永久代的初始容量,-XX:MaxPermSize是永久代的最大容量,建议初始容量为最大容量

    类元数据 metaspace

    • jdk8中,永久代被完全移除了,相关参数 -XX:PermSize和-XX:MaxPermSize被忽略并改用metaspace
    • -XX:MetaspaceSize是类元数据的初始容量(默认20.8M左右(x86下开启c2模式),主要是控制metaspaceGC发生的初始阈值,也是最小阈值,但是触发metaspaceGC的阈值是不断变化的,与之对比的主要是指Klass Metaspace与NoKlass Metaspace两块committed的内存和)
    • -XX:MaxMetaspaceSize是类元数据的最大容量(默认基本是无穷大,但是我还是建议大家设置这个参数,因为很可能会因为没有限制而导致metaspace被无止境使用(一般是内存泄漏)而被OS Kill。这个参数会限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的内存大小,会保证committed的内存不会超过这个值,一旦超过就会触发GC,这里要注意和MaxPermSize的区别,MaxMetaspaceSize并不会在jvm启动的时候分配一块这么大的内存出来,而MaxPermSize是会分配一块这么大的内存的。)
    • -XX:CompressedClassSpaceSize是设置Class Metaspace的大小(默认1G,这个参数主要是设置Klass Metaspace的大小,不过这个参数设置了也不一定起作用,前提是能开启压缩指针,假如-Xmx超过了32G,压缩指针是开启不来的。如果有Klass Metaspace,那这块内存是和Heap连着的)

    JVM源码分析之Metaspace解密

    实例解析

    示例: -Xms300M -Xmx300M -XX:NewRatio=2 -XX:SurvivorRatio:8 -XX:PermSize=100M -XX:MaxPermSize=100M
    解析:
    永久代固定尺寸为 100M;
    整个堆固定尺寸为 300M,其中“老年代 / 新生代”为-XX:NewRatio=2,所以老年代为 200M,新生代为 100M;
    新生代总共 100M,其中“Eden / Survivor0”为-XX:SurvivorRatio=8,所以 Eden 为 80M,Survivor0=Survivor1=10M。

    Jdk8之后应该是 -Xms300M -Xmx300M -XX:NewRatio=2 -XX:SurvivorRatio:8 -XX:MetaspaceSize=100M -XX:MaxMetaspaceSize=100M

    参数 含义 值示例
    Xms jvm启动时分配的内存 -Xms300m,表示分配300M
    Xmx jvm运行过程中分配的最大内存 -Xms300m,表示jvm进程最多只能够占用300M内存
    Xss jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M -Xss1M
    XX:NewRatio 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=2
    XX:SurvivorRatio 设置年轻代中Eden区与Survivor区的大小比值 -XX:SurvivorRatio:8
    XX:PermSize 非堆内存初始值,默认是物理内存的1/64 -XX:PermSize=100M
    XX:MaxPermSize 设置最大非堆内存的大小,默认是物理内存的1/4 -XX:MaxPermSize=100M
    XX:MetaspaceSize 指定元空间的初始大小 -XX:MetaspaceSize=100M
    XX:MaxMetaspaceSize 指定元空间的最大值大小 -XX:MaxMetaspaceSize=100M

    jvm的内存分配策略

    • 对象优先分配在eden,若eden空间不足,则引发YoungGC(YGC),
    • 如果幸存对象(被根对象直接或者间接引用),能存放在幸存者区,则存活的对象全部存放在幸存者区
    • 如果幸存对象在幸存者区放不下,则存活的对象全部移入老年代
    • 如果老年代空间不足,则发起一次FullGC(FGC),整个系统停顿,老年代也将参与回收
    • 如果FullGC空间仍然周转不过来,则报 OutOfMemoryError并导致进程结束
    • 如果FGC和YGC能周转过来,则新对象分配在eden
    • 大对象直接分配在老年代(所谓的大对象是指,需要大量连续内存的java对象,尤其是长字符串或数组,比如 byte[n] 对象; 可以指定多大才算大对象(只对 Serial/ParNew 有效)
      -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1024000
      设置对象大小为,1024000 即 1M

    不同的gc方式
    -XX:+UseParallelGC:代表新生代使用Parallel收集器,老年代使用串行收集器。Parallel Scavenge收集器在各个方面都很类似ParNew收集器,它的目的是达到一个可以控制的吞吐量。吞吐量为CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机运行100分钟,垃圾收集花费1分钟,那吞吐量就99%。Server模式默认开启,其他模式默认关闭
    -XX:+UseSerialGC:使用串行回收器进行回收,这个参数会使新生代和老年代都使用串行回收器,新生代使用复制算法,老年代使用标记-整理算法。Serial收集器是最基本、历史最悠久的收集器,它是一个单线程收集器。一旦回收器开始运行时,整个系统都要停止。Client模式下默认开启,其他模式默认关闭
    -XX:+UseParNewGC:Parallel是并行的意思,ParNew收集器是Serial收集器的多线程版本,使用这个参数后会在新生代进行并行回收,老年代仍旧使用串行回收。新生代S区任然使用复制算法。操作系统是多核CPU上效果明显,单核CPU建议使用串行回收器。打印GC详情时ParNew标识着使用了ParNewGC回收器。默认关闭
    其他类型的垃圾收集

    • 长期存活对象移入老年代,经历n次YGC仍然存活的对象,下次YGC时将被移入老年代;可以设置该数值:-XX:MaxTenuringThreshold=15,默认15次
    • 永久代满了也会导致 FullGC,老年代、永久代的垃圾收集是捆绑在一起的,因此无论两者谁满了,都会触发两者的FullGC
    • 动态对象年龄判定:如果 Survivor 相同年龄对象占用空间达到一半,则大于等于该年龄的对象都移入老年代
    • 冒险模式:JDK 6u24 之后,总是开启冒险模式(先尝试 YGC,以免 FGC 频繁);每次 YGC 之前,如果老年代最大连续空间,大于新生代所有对象空间之和,则 YGC 肯定成功。否则:如果老年代最大连续空间,大于历次晋升老年代的平均值,则先冒险尝试 YGC;如果老年代最大连续空间,小于历次晋升老年代的平均值,则直接 FullGC;

    JVM优化原则

    • 尽量减少 YoungGC
    • 尽量减少 FullGC:一天最多 FullGC 一次,最好在系统空闲期(如深夜);

    优化方法

    • 代码角度:缩短对象生命期,尤其是大对象
    • JVM参数角度:优化JVM参数以减少YGC/FGC次数,可替换收集器

    服务端开启 JMX/jstatd

    这两项功能必须开启,可视化工具 VisualVM 会用到
    visualvm安装连接jmx和jstatd

    开启后,发现其中一台服务器gc异常

    gc异常

    可以看见 非常频繁,应该是由于长连接都连在了同一台服务器上导致的,eden完全不够用

    • 机器内存为8g,非本机唯一应用,但是是本机需要消耗最多的应用,故先分配3g(3072M)看下使用效果
    • 永久代只需 130M,可指定初始和最大值为256M,避免反复伸缩的开销
    • 整个堆(新生代+老年代)目前内存为 2400M,可增加至 384M,指定初始和最大值都为 384M,避免反复伸缩的开销
    • YoungGC 过于频繁,原因是 Eden 区过小。老年代/新生代 目前比例为 -XX:NewRatio=2,看图形可知,老年代中长寿对象并不多,可缩减老年代让给新生代,所以调整 -XX:NewRatio=1
    • Eden / Survivor0比值目前为 -XX:SurvivorRatio=8,暂不调整
    • 优化后的jvm参数 -Xms3072M -Xmx3072M -XX:NewRatio=1 -XX:SurvivorRatio=8 -XX:PermSize=256M -XX:MaxPermSize=256M

    优化后的效果


    jvm优化后

    图像是好看了一点,但是发现YGC还是很频繁,需要寻找下一步的解决办法

    jdk8不支持下列参数
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=256M; support was removed in 8.0
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0

    白天的时候,愈加明显,因为分配给api-server1的Eden空间比较大,所以他的ygc还是挺正常的

    admin-server1
    api-server1
    admin-server2
    api-server2

    因为admin-server1和api-server1是在同一台服务器,准备新增服务器进行服务拆分处理

    拆分后默认配置下的


    image.png
    image.png

    修改配置后的(扩容)
    -Xms6144M -Xmx6144M -XX:NewRatio=1 -XX:SurvivorRatio=8


    image.png
    image.png
    需要找到原因,为什么2台服务器的资源分配不均匀,并且现在使用的资源那么多(这个需要之后考虑下)

    运行一段时间后,因为负载均衡把连接分发均匀了


    image.png
    image.png

    参考:
    JVM 优化实战

    相关文章

      网友评论

          本文标题:jvm调优记录

          本文链接:https://www.haomeiwen.com/subject/tvyunhtx.html