JAVA虚拟机原理与调优

知识要点:
  JVM的两个子系统:类加载子系统、执行引擎子系统

  JVM的两个组件:运行时数据区域、本地接口组件

  3种生命周期:CLASS生命周期、对象生命周期

 

 

 

  Class生命周期:

  加载 =》验证 =》准备 =》解析 =》初始化 =》使用 =》卸载

  loading:加载 类加载器进行加载

  verification:验证,验证class文件是否符合JVM规范

  preparation:准备,给类静态变量分配存储空间:基本类型为0,引用为null。不会进行初始化。

  解析:将符号引用转成直接引用,

  初始化:对静态变量赋值,运行静态代码块。

 

  对象生命周期:
  分配内存 =》初始化内存 =》设置对象 =》执行初始化代码 =》使用 =》GC。

  

  JAVA源码   ======》CLASS文件 =====》类加载器

  JVM类加载器: 

    类加载器根据指定的类名来装载class文件的内容  到 ====》运行时数据区域中的MetaSpace中,并在                               MetaSpace中为其生成代表该类的Class对象。

  

  执行引擎子系统:

    Java是使用解释器+编译器共存的模式工作的。

    当执行引擎获取到由javac编译后的。class字节码文件后,在运行时是通过解释器转换成最终的机械码执行。
    为了提升效率,JVM加入一种名为JIT即时编译的技术,即时编译器目的是为了避免一些经常执行的代码被解                释执行,JIT会将整个函数编译为平台本地的机械码,从而在很大程度上提升执行效率。

  

  JAVA虚拟机GC算法:
    两种原理:引用计数法、根搜索算法

    四种GC算法:标记-清除、复制、标记-整理、标记-清除-整理

   标记清除:

   1. 标记:从根集合开始扫描,对存活对象进行标记

   2. 清除:扫描整个内存空间,回收被标记的内存对象。  会产生内存碎片

  复制算法:
    1. 从GC Root开始,通过根搜索算法从From 中找存活对象,拷贝到To中

    2. From,TO交换身份,下次内存分配从To开始,需要双倍空间

  压缩算法:
   1. 标记:根集合开始扫描,对存活对象进行标记
   2. 压缩: 再次扫描,并且将存活对象往一端滑动,清除需要回收的对象,获得连续内存区域。

   需要移动对象,成本较高。

  标记-清除-压缩算法:
    1. 标记清除和标记压缩算法的结合

    2. 多次标记清除后,才进行一次内存空间的压缩。  优点:减少了移动对象的成本,不会有大量的空间碎片

  GC作用区域:元空间和堆。

 

  Hotspot的JVM通过分代进行内存管理

  两种分代:新生代、年老代

  分代管理:
  GC类型:Minor GC 针对新生代GC

        Major GC 针对老年代GC

        Full GC 针对元空间、新生代、老年代三者的GC

  新生代

  

 

   由Eden,两块大小相通的Survivor(s0和s1)构成,to总是为空

  一般在Eden分配对象,优化 使用TLAB (Thread Local Allocation Buffer) 

  理由:在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独

  拥有一个空间,如果需要分配内存,在自己空间上分配,这样就不存在竞争情况,不用加锁,可以大大提升分配           效率。

  值得注意的是,只是在分配这个动作是线程独享的,至于在读取、垃圾回收动作上都是线程共享的。

  保存80% -90%生命周期较短的对象,GC频率高,采用效率较高的复制算法(存活的对象不多,使用复制算法比较合理)

  老年代

  存放新生代中经历多次GC依然存活的对象

  新建对象也有可能直接在老年代分配,取决于具体的GC实现和参数设置

  GC 频率相对较低,标记、清理、压缩算法各种结合和优化。

 

  这其中有几个参数需要记忆一下:

  -Xmx : max 堆,表示堆区内存可被分配的最大上限

  -Xms : start 堆 ,表示虚拟机堆区内存初始内存分配大小

  -Xss : stack size 表示线程栈的大小

  垃圾回收器:
  

 

 

 串行(Serial) VS  并行(Parallel)

 

 

 判断的GC线程是否串行还是并行。

在单核CPU下并行可能更慢

STW(Stop the world) VS  并发 (Concurrent) 

 

 

 STW:暂停整个应用,时间可能会比较长

并发(Concurrent) :更复杂,GC可能会抢占应用CPU

 

新生代垃圾回收器:

都使用复制算法,原理上一致:

  拷贝eden和from中存活的对象到to中

  部分对象因为某些原因晋升到old中

  清空eden和from,from和to交换身份直到下一次GC发生

  

1. Serial Copying:

特性 serial、STW,单CPU、新生代小、对暂停时间要求不高的应用

对象直接分配在Old的情况: a. 对象大小超过eden space 大小   b. 大对象 (设置 PretenureSizeThresholder可设置)

晋升规则:a.经历多次minor gc 任存活的对象  b. To Survivor 放不下的(满或这剩余空间不够)对象直接晋升

3. ParNew 

特性:

Parallel、STW
可以认为是Serial Copying的多线程版本,各项特征与之基本一致
可以搭配 CMS
不可搭配Parallel Old
3. Parallel Scavenge 

问题:有了ParNew 为何还要有Parallel Scanvenge?
他有一个关注的重点,是吞吐量(用户执行时间除以总时间)

首先设置参数:UseAdaptiveSizePolicy 这个参数意味着jvm帮你配置好

会有两个参数

-XX:MaxGCPauseMillis 设置最大GC暂停时间目标,是一个软目标,尽最大努力来是实现它。慎用,此值越大吞吐量越大

-XX:GCTimeRatio 吞吐量的值,也就是垃圾收集时间占总时间的比率。

当设置这三个参数之后,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应调节策略。

使用Parallel Scanvenge 收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成,只需要设置基本参数,然后使用MaxGCPauseMillis 或者GCTimeRatio 参数给虚拟机设立一个优化目标,那具体细节参数调节工作

就由虚拟机完成了。 自适应调节策略也是Parallel Scanvenge收集器与ParNew 收集器的一个重要区别。

其特性:
Parallel(并行)、STW

并行线程数默认值:
  CPU核数<=8 :=cpu数

  >8时:(3+CPU核数*5)/8

会根据minor GC 频率、动态调整Eden/S0/S1大小(-XX:UseAdaptiveSizePolicy)

适合场景:

  多CPU、对暂停时间要求短的应用

  是Server级别机器上的默认选择

  

对象直接分配在Old的情况:

  在TLAB和eden上分配失败,且对象大于eden的一半大小

  设置对象大小参数无效

晋升规则 (和其他一样)

  经历多次minor gc 仍然存活的对象

  To Survivor 放不下的 对象直接晋升

 

老年代垃圾收集器 : Serial Old、Parallel Old、CMS

Serial Old

特性:
Serial、Stop-the-world

使用算法 Mark-Sweep-Compact(标记清除压缩算法)

由于是单线程,GC造成的暂停时间可能会较长。

 

Parallel Old

特性:Parallel、Stop-the-world

      使用算法Mark-Compact  (标记压缩算法)

适用场景

  多核CPU、对暂停时间较为敏感的应用

  是Server级别(2核CPU 2G内存)机器的默认选择

  这部分回收流程知识,网上没有相应的介绍。

 

CMS 老年代回收器:

  特征:Parallel(并行)、Concurrent(并发)

  使用算法 Mark-Sweep

  缩短暂停时间,但是相当复杂,增加GC总时间

  默认并发数= (新生代并行GC线程数+3)/4,可用-XX:ParallelCMSThreads =2 来指定

  适用场景:暂停时间短,对追求最快响应速度的应用。 (每次GC造成的应用程序暂停)

  CMS运行流程如下:
  

 

 

 其缺点

  与应用抢CPU(因为有并发)

  GC总耗时长(因为并发所以会比较长)

  产生浮动垃圾

    Concurrent Sweep 阶段有新的垃圾产生,只能下一次被收集

    GC同时应用程序也在运行,需要Old预留空间,达到一定比例即触发GC

       -XX:CMSInitiatingOccupancyFraction =  

    预留空间不够,出现Concurrent Mode Failure

  内存碎片

    Free list 中没有合适的内存空间来分配对象,只能触发Full GC:

      -XX:+UseCMSCompactAtFullCollection 一般默认为true  每次full GC后进行压缩

      -XX:CMSFullGCsBeforeCompaction =    多少次full GC后进行压缩

  组合选择:
    单CPU或者小内存,单机程序

      -XX:+UseSerialGC  

    多CPU,需要最大吞吐量,如后台计算型应用

      -XX:+UseParalleGC
      -XX:   +UseParallelOldGC

    多CPU,低停顿时间,需要快速响应如互联网应用

      -XX:+UseConcMarkSweepGC

      -XX:  +ParNewGC

    

    自动选择GC方式

      吞吐量优先        -XX:+GCTimeRatio = n

      暂停时间优先    -XX:+MaxGCPauseMillis =n

 

  JVM调优与监控分析工具

    四种调优 :堆大小调优、新生代大小调优、新生代晋级、年老代调优
    五个工具 : JVM辅助参数、JDK命令行工具、JConsole、VisualVM MAT

  一般来说,堆越大越好

    降低GC频率,但过大可能会增加单次GC的时间

    对象更有可能成为垃圾

    

    参数:因堆每次调整都会触发一次Full GC 避免调整频繁,可设置 -Xms = -Xmx

    -Xms = 1024:堆的最小值

    -Xmx = 1024 : 堆的最大值

    当然也不总是这样

    设置-Xms 为堆的预期大小

       堆调整代价很大

    如果内存允许,设置-Xmx为一个比-Xms更大的数值:

      为什么要设置呢,为了以防外一,因为系统负载比你想象的更重要,随着时间推移,数据量越来越大,此时进行一次Full GC和堆调整,总比发生OOM和宕机好 

 

    增大Eden的大小会

      降低Minor GC的频率

      但不一定会增大minor GC的时间:Minor GC的耗时和要拷贝的对象数量,即存活数量的多少成正比。

      处于性能的考虑,一般使用-Xmn来固定新生代大小

    

    尽可能让对象呆在Survivor中,使之在新生代被回收

      减少晋升到老年代的对象

      降低年老代GC的频率

    但同时,避免长时间存活的对象在Survivor间不必要的拷贝

      增加了MinorGC不必要的开销

    不容易找到平衡点

      原则上:能多复制要优于晋升

    

    新生代晋升调优

      对象晋升年龄的阈值:Tenuring Threshold 

        年龄标志位age,每熬过一轮GC,对象年龄增加1

        Serial Copying 和 ParNew 每次Minor GC后重新计算Tenuring Threshold 的规则:

          有一个参数 -XX:TargetSurvivorRatio = n        GC后Survivor预期被占用的比例

          计算 Desired Survivor Size = Survivor大小 * TargetSurvivorRatio

          统计存活对象的年龄,若某个年龄及一下对象总和  > Desired Survivor Size,则 Tenuring Threshold = min(该年龄,MaxTenuring Threshold(也是设置的值))

          否则 TenuringThreshold = MaxTenuringThreshold

          下次Minor GC的阈值依次为准。  当你开启-XX:+PrintTenuringDistribution的时候,你会发现TenuringThreshold 从 会在变化。

    老年代调优:

      尽可能优先调优新生代

      对CMS,在不紧要的时间段手动进行Full GC :标记清除压缩

      大小的平衡:太大、单次GC时间长   太小,GC频率高

      硬件优化:不表:CPU、物理内存啥的

      代码优化啥的,避免无用的对象浪费Old空间

  

    5个工具之JVM辅助参数:

      -XX:PrintGC 输出GC的简要信息

      -XX:PrintGCDetails 输出GC详细信息

      -XX:PrintGCTimeStamps  输出GC时间戳

      -XX:PrintGCApplicationStoppedTime  输出GC暂停时间

      -Xlogg c/gc.log  输出GC信息到文件

    

    命令行工具

      jps     JVM Process Status Tool, 显示指定系统内所有HotSpot 虚拟机进程   

      jstat   JVM Statistics Minitoring Tool, 用于收集HotSpot 虚拟机各方面的运行数据  状态监控工具   (主要是监控工具)

      jinfo    Configuration Info for Java, 显示虚拟机配置信息

      jmap   Memory Map for Java, 生成虚拟机的内存转储快照(heapdump)文件

      jstack  Stack Trace for java,    显示虚拟机的线程快照

 

    JConsole:JVM监视与管理  bin目录下就有

    

 

              另外一个是:visual VM

     

    jvisualvm 是jdk 1.6以后带上的工具,是jconsole的升级版本。   

      

    

    

      

  

  

  

 

 

 

 

 

  

  

  

posted @ 2022-06-17 14:46  小罗咯  阅读(172)  评论(0)    收藏  举报