JVM优化


概述

本文主要介紹JVM以及JVM优化和FULL GC 的处理.

什么是JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM HEAP分区

JVM HEAP可分为三大区新生代- young generation、老年代 - old generation、永久代 - Permanent generation(也叫方法区).

young generation 又分为 Eden区和两个survivor区(FROM SURVIVOR 和 TO SURVIVOR),两个survivor区存放每次垃圾回收后存活的对象.

young generation主要用于存放new出来的对象,长时间未被GC回收的会进入到survivor区,如果survivor区对象长时间未被回收则会被提升到老年区,老年区一般存放比较大的对象,至于Permanent generation 则存放静态对象例如static。

permanent generation管理class文件、静态对象、属性等。

JVM 垃圾回收机制

JVM垃圾回收机制采用“分代收集”:新生代采用复制算法,老年代采用标记清理算法。

JVM 复制(Copying)算法

将内存平均分成A、B两块,算法过程:

  1. 新生对象被分配到A块中未使用的内存当中。当A块的内存用完了, 把A块的存活对象对象复制到B块。
  2. 清理A块所有对象。
  3. 新生对象被分配的B块中未使用的内存当中。当B块的内存用完了, 把B块的存活对象对象复制到A块。
  4. 清理B块所有对象。
  5. goto step 1

算法优化:使用Eden/S0/S1三个分区

如上述过程,如果平均分成A/B块太浪费内存,采用Eden/S0/S1三个区更合理,空间比例为Eden:S0:S1==8:1:1,有效内存(即可分配新生对象的内存)是总内存的9/10。

算法过程:

  1. Eden+S0可分配新生对象;
  2. 对Eden+S0进行垃圾收集,存活对象复制到S1。清理Eden+S0。一次新生代GC结束。
  3. Eden+S1可分配新生对象;
  4. 对Eden+S1进行垃圾收集,存活对象复制到S0。清理Eden+S1。二次新生代GC结束。
  5. goto step 1

默认Eden:S0:S1=8:1:1,因此,新生代中可以使用的内存空间大小占用新生代的9/10。

为什么不直接分成两个区,一个区占9/10,另一个区占1/10,这样做的原因大概有以下几种

1.S0与S1的区间明显较小,有效新生代空间为Eden+S0/S1,因此有效空间就大,增加了内存使用率.

2.有利于对象代的计算,当一个对象在S0/S1中达到设置的XX:MaxTenuringThreshold值后,会将其分到老年代中,设想一下,如果没有S0/S1,直接分成两个区,该如何计算对象经过了多少次GC还没被释放,你可能会说,在对象里加一个计数器记录经过的GC次数,或者存在一张映射表记录对象和GC次数的关系,是的,可以,但是这样的话,会扫描整个新生代中的对象, 有了S0/S1我们就可以只扫描S0/S1区了

JVM调优配置及参数说明

    CMD java
    //1 非标准参数配置
    -Xms1024m 
    //指定jvm堆的初始大小,默认为物理内存的1/64,最小为1M;可以指定单位,比如k、m,若不指定,则默认为字节。 
    -Xmx1024m 
    //指定jvm堆的最大值,默认为物理内存的1/4或者1G,最小为2M;单位与-Xms一致。
    -Xmn480m
    //JVM中年轻态的大小 年轻态比老年态为1/1  持久态最好为64M  老年态为480 FULLCG次数会较少 但是每次FULLCG时间会较长
    -Xss256K  
    //单个线程栈的大小 一般默认512k

    //2 非stable参数
    -XX:PermSize=64M                    
    //持久态的大小 JDK8是另一个参数设置持久态
    -XX:MaxPermSize=64M 
    //设置持久代最大值为100m,默认为xms的1/8。MaxPermSize过小会导致:java.lang.OutOfMemoryError: PermGen space
    //32位Linux和64位Linux好像不一样,64位系统似乎只要配置MaxTenuringThreshold参数。
    #-XX:MaxTenuringThreshold=20 
    //MaxTenuringThreshold=0就是去掉了救助空间 MaxTenuringThreshold去掉,这样即没有暂停又不会有promotoin failed,而且更重要的是,年老代和永久代上升非常慢(因为好多对象到不了年老代就被回收了),所以CMS执行频率非常低,好几个小时才执行一次
    -XX:SurvivorRatio=4             
    //Eden区与Survivor区的大小比值4/1

    /**适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
    响应时间优先的应用:年轻代大小选择 
    尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。 
    响应时间优先的应用:年老代大小选择 
    年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得: 
    **/

    -XX:+UseConcMarkSweepGC               
    //对老生代采用并发标记交换算法进行GC
    -XX:+CMSParallelRemarkEnabled        
    // 降低标记停顿
    -XX:CMSFullGCsBeforeCompaction=0      
    //由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行n次FullGC以后对内存空间进行压缩、整理。
    -XX:CMSInitiatingOccupancyFraction=70 
    //表示年老代空间到70%时就开始执行CMS,确保年老代有足够的空间接纳来自年轻代的对象。 满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn就不会出现promotion 内存太低不适应
    -XX:+UseCMSCompactAtFullCollection    
    //使用并发收集器时,开启对年老代的压缩,failed
    -XX:+DisableExplicitGC                
    //禁止System.gc(),免得程序员误调用gc方法影响性能; 
    -XX:+UseParNewGC                      
    //对年轻代采用多线程并行回收,这样收得快;

    //3 日志打印信息
    -XX:+HeapDumpOnOutOfMemoryError  
    -XX:HeapDumpPath=/usr/aaa/dump 
    -Xloggc:/usr/local/mylog/heap_trace.txt 
    -XX:+PrintGCTimeStamps 
    -XX:+PrintGC 
    -XX:+PrintHeapAtGC
    -XX:+PrintGCDetails
    -Duser.timezone=Asia/Shanghai
    -jar yourjarName.jar

JVM 如何测试调优后的性能

对于linux可以使用TOP命令查看内存和CPU占用,在服务高负荷状态下,尽量出发FULL GC 查看GC日志,看new generation和old generation的GC次数和GC后的结果是否达到预期,来调试参数的配比.

    jmap -dump:format=b,file=heap.bin [pid]

分析工具可以使用MAT.

总结

一盏灯, 一片昏黄; 一简书, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。



   Reprint policy


《JVM优化》 by jackromer is licensed under a Creative Commons Attribution 4.0 International License
 Previous
DOCKER镜像启动报no such file or directory问题 DOCKER镜像启动报no such file or directory问题
概述 在通过dockerfile搭建docker镜像,启动镜像时常常会报no such file or directory提示,今天说明一下异常产生的原因。 原因分析 原因大概分为两类,第一类确实无该文件,第二类文件编码问题 1.文件
2019-08-27
Next 
DOCKER常用命令 DOCKER常用命令
概述 主要介绍docker中常用的一些命令 常用命令 docker build -t xxxx:1.0.0 . //docker 打包镜像,最后的“ .”必须要 docker push xxxx:1.0.0 //doc
2019-08-27
  目录