• JVM调优基础


    一、JIT

    java同时具有解释型语言的跨平台性,又有编译型语言的本地性能
    两次编译:第一次被编译成java字节码,JVM可以直接解释执行Java字节码,也可以用JIT先编译成本地二进制汇编语言,再执行

    -client:运行开始时就编译
    -server:运行后编译,可以更好地进行优化
    -XX:TieredCompilation 分层编译,先client-再server java8默认开启 java8之前分层编译有BUG
    在64位JVM使用-client,JVM仍会使用-d64
    而在32位JVM使用-d64, 会报错

    代码缓存:JIT编译后的二进制代码的缓存,当缓存满时,新的代码只能解释执行
    满时警告:CadeCache is full
    调整大小:-XX:ReservedCodeCacheSize=N java7默认96M
    其他:编译阈值等用默认

    当方法或循环达到编译阈值时,就会进入编译队列,异步进行代码编译,此时代码仍可解释执行;当下次调用方法或者循环进入下一轮时,会调用编译后的代码。

    内联:热点的getter, setter在编译时会被直接改成p.name对对象变量的引用
    默认开启

    逃逸优化:对于不存在线程共享的同步代码,会去掉synchronized

    逆优化:丢弃编译后的二进制代码
    先前优化的代码失效时:对象已经改变
    分层编译:client——server切换
    僵尸代码:长时间没有使用的代码会被丢弃

    二、GC

    GC调优主要是减少GC的stop-the-world时间
    一、内存划分
    young=eden+survivor
    1.Eden满后执行Minor GC, 存活的对象被堆积在同一Survivor
    2.当一个Survivor满后,其中存活的对象全部进入另一个Survivor,同时对象年龄+1,
    此过程中,年龄达到MaxTenuringThreshold (最大/占据/门槛)的对象进入Old
    -->必有一个Survivor是空的
    一些较大的对象,需要直接分配一块较大的连续内存空间,则直接进入到老年代

    old
    Full GC - Major GC 时间是Minor GC 10倍以上
    老年代对新生代对象的引用:
    老年代中存在一个cardtable,所有老年代对象指向新生代对象的引用都会被记录在这个表中。
    当新生代执行GC的时候,只需要查询card table来决定是否可以被回收,而不用查询整个old

    permanent - 持久代又称方法区
    保存常量、字符串常量、class对象
    class对象被回收的条件十分严苛:
    1 所有实例被回收
    2 加载该类的ClassLoader被回收
    3 Class对象无法通过任何途径访问(包括反射)

    java8中将永久代变为了元空间:
    元空间只保存对JIT和JVM运行有关的数据,Class,反射对象等被转移到普通堆空间了
    元空间可以使用的内存是无限的,不需要参数调整

    二、GC算法
    1、根搜索算法 - 从root引用不到的对象,为不可达对象,将被GC
    可以作为Root的对象有:
    虚拟机栈中引用的对象-本地变量表
    本地方法栈中引用的对象-Native方法
    方法区静态属性引用的对象
    方法区常量引用的对象
    2、标记-清除算法 - 先扫描所有对象,标记所有存活,再清除所有未被标记的对象
    在存活对象很多时适合,因为不用移动存活对象
    不会移动对象,高效但会导致内存碎片
    3、复制算法 - 将内存分为两个区,每次只使用一个区
    在存活对象很少时适合,因为要移动所有存活对象
    50%的内存浪费
    4、标记-整理算法 - 比2多了一步整理
    Major GC一般使用标记-整理算法
    Minor GC一般使用复制算法

    三、回收器 - 每个回收器都存在stop-the-world问题
    1、Serial (-XX:+UseSerialGC) 用于年轻代
    使用:复制算法
    2、SerialOld (-XX:+UseSerialGC) 用于老年代
    使用:标记-整理算法
    *3、ParNew (-XX:+UseParNewGC) 用于年轻代
    使用:复制算法
    Serial的多线程版本
    *4、ParallelOld(-XX:+UseParallelOldGC) 用于老年代
    使用:标记-整理算法
    Serial的多线程版本
    吞吐量优先
    5、ParrallelScavenge (-XX:+UseParallelGC) 用于年轻代
    使用:复制算法
    可控吞吐量(吞吐量=用户代码执行时间/用户代码执行时间+GC时间
    *6、CMS (-XX:+UseConcMarkSweepGC) 用于老年代
    *使用:标记-清理算法
    初始标记(STW) - 并发标记 - 并发预清理 - 重新标记(STW) - 并发清理 - 并发重置
    初始标记:STW-找到和根对象直接关联的对象
    并发标记:找到和初始标记对象关联的对象
    并发预处理:找到在并发标记阶段晋升的对象,减少下一阶段重新标记的工作量
    重新标记:STW-重新从根对象开始查找并标记并发阶段遗漏的对象
    并发清理:--
    并发重置:重置CMS状态

    缺点:
    内存碎片
    GC线程和应用线程并发执行,消耗更多CPU资源,吞吐量低(存在线程切换)
    内存浪费-默认68%就出发GC XX:CMSinitiatingOccupandcyFraction=n

    如果CMS后台线程无法获得完成GC的CPU资源,或者内存碎片严重无法找到连续空间分配内存,
    CMS会蜕化成Serial收集器,进行一次Serial收集。之后又变回CMS收集

    7、G1收集器 - 用于老年代和年轻代,是商用收费的收集器
    适合处理超大堆
    将堆划分为若干个区域Region
    老年代也被划分为多个Region,采用复制算法

    参数调整
    1.堆大小
    -Xms4096m
    -Xmx4096m
    2.代空间
    -XX:NewRatio=N youngSize = heapSize / 1+N

    -XX:NewSize=1024m -XX:MaxNewSize=1024m 显示指定young代大小

    永久代
    -XX:PermSize=N -XX:MaxPermSize=N
    元空间
    不需要调整
    3.收集器
    吞吐量优先:
    -XX:+UseParallelGC
    -XX:+UseParallelOldGC
    响应优先:
    -XX:+UseParNewGC
    -XX:+UseConcMarkSweepGC
    参数:
    自动调整:

      JVM运行过程中,会根据以往的性能历史,自动调整堆、代已经Survivor大小,

      默认开启,可以用-XX:-UseAdaptiveSizePolicy关闭

      如果将-Xms -Xmx设置成一样的值,-XX:NewSize -XX:MaxNewSize设置成一样的值,那么自动调节功能将被关闭
    -XX:ParallelGCThreads 会根据CPU核心数自动调整
    日志:
    -XX:+PrintGCDetails 打印详细GC日志
    -XX:+PrintGCTimeStamps 打印GC日志生成的时间
    -Xloggc:filename 限定GC日志路径,默认是标准输出(命令行)

  • 相关阅读:
    屏幕适配问题
    对八大排序算法的补充说明
    基数排序
    归并排序
    快速排序
    简单选择排序
    堆排序
    希尔排序
    直接插入排序
    (转)Android APK反编译详解
  • 原文地址:https://www.cnblogs.com/bianzy/p/6557564.html
Copyright © 2020-2023  润新知