jdk1.8--JVM分析与调优

不少文章都是讲如何配置JVM各个参数的,可是真正生产环境中,各个参数的值应该按什么套路配,却鲜有说起。
本文分四个部分,分别是JVM说明,GC的过程、参数配置和具体配置值。

1、JVM空间说明

在JDK1.7及之前,HotSpot虚拟机将java类信息、常量池、静态变量、即时编译器编译后的代码等数据,存储在Perm(永久带)里(对于其余虚拟机如BEA JRockit、IBM J9等是不存在永久带概念的),类的元数据和静态变量在类加载的时候被分配到Perm里,当常量池回收或者类被卸载的时候,垃圾收集器会回收这一部份内存,但效果不太理想。算法

JDK1.8时,HotSpot虚拟机对JVM模型进行了改造,将类元数据放到了本地内存中,将常量池和静态变量放到了Java堆里,HotSpot VM将会为类的元数据明确的分配与释放本地内存
在这种架构下,类元数据就突破了-XX:MaxPermSize的限制,因此此配置已经失效,如今可使用更多的本地内存。这样必定程度上解决了原来在运行时生成大量的类,从而常常Full GC的问题——如运行时使用反射、代理等。

 

 

干货:能够发现最明显的一个变化是元空间从虚拟机转移到了本地内存。默认状况下,元数据空间大小仅受限于本地内存,
这意味着之后不会由于永久代大小不够而抛出OOM异常了。
jdk1.8之前,HotSpot VM将class和类的jar包数据存储在PermGen里,
PermGen大小是固定的,并且项目之间没法公用公有的class,因此很容易碰到OOM异常。改为MateSpace后,
各个项目会共享一样的class空间。好比多个项目都引用了apache-common包,
在MateSpace中只会存储一份的apache-common的class,提升了内存的利用率,垃圾回收更有效。

2.JVM参数配置

内存分配策略:
大多数状况下,对象在新生代的Eden中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC,而大对象(须要大量连续内存空间的Java对象,相似长字符串和数组)将经过分配担保机制直接进入老年代。数组

Minor GC——复制算法具体过程:架构

    1. 将Eden和S0中还存活着的对象一次性的复制到S1中,而且清理掉Eden与S0的空间。若是S1放不下还存活着的对象,那这些对象将经过分配担保机制进入老年代。【原理上随时保持S0和S1有一个是空的,用来存下一次的对象】
    2. Eden区快满的时候,会进行上一步相似操做,将Eden和S1区的年纪大的对象放到S0区【此时S1区就是空的】
    3. 直到Eden区快满,S0或者S1也快满的时候,这时候就把这两个区的年纪大的对象放到Old区。
    4. 依次循环,直到Old区也快满的时候,Eden区也快满的时候,会对整个这一块内存区域进行一次大清洗(FullGC),腾出内存,为以后的对象建立,程序运行腾地方
新生代GC(Minor GC):指发生在新生代的垃圾回收动做,由于java对象大多具有朝生夕灭的特征,因此Minor GC发生的特别频繁,
通常回收速度也很快。
老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,至少会伴随一次的MinorGC(但非绝对,
在Parallel Scavenge收集器的收集策略里就有直接进行Minor GC的策略选择过程)。Major GC的速度通常比Minor GC慢10倍以上。

  3、JVM参数配置在jdk1.8之前,生产环境通常有以下配置jvm

-XX:PermSize=512M -XX:MaxPermSize=1024M

表示在JVM里存储Java类信息,常量池和静态变量的永久代区域初始大小为512M,最大为1024M。在项目启动后,这个值是固定的,若是项目class过多,极可能遇到OutOfMemoryError: PermGen异常。

升级JDK1.8以后,上面的perm配置已经变成工具

-XX:MetaspaceSize=512M XX:MaxMetaspaceSize=1024M

  MetaspaceSize若是不作配置,经过jinfo查看默认MetaspaceSize大小(约21M),MaxMetaspaceSize很大很大,前面说过MetaSpace只受本地内存大小限制。

jinfo -flag MetaspaceSize 1234  #结果为:-XX:MetaspaceSize=21807104
jinfo -flag MaxMetaspaceSize 1234 #结果为:-XX:MaxMetaspaceSize=18446744073709547520

  

干货: MetaspaceSize为触发FullGC的阈值,默认约为21M,如作了配置,最小阈值为自定义配置大小。空间使用达到阈值,触发FullGC,同时对该值扩大。固然若是元空间实际使用小于阈值,在GC的时候也会对该值缩小。
MaxMetaspaceSize为元空间的最大值,若是设置过小,可能会致使频繁FullGC,甚至OOM。

3.GC(GARBAGECOLLECTION)过程

首先贴一张网上盗来的大图,用它来说明下GC的过程再合适不过。

 

  1. 新new的对象都放在Eden区(伊甸园嘛,创造的地方
  2. Eden区满或者快满的时候进行一次清理(Minor Gc),不被引用的对象直接被干掉;还有引用的对象,但是年龄比较大的,挪到S0区
  3. 下次Eden区快满的时候,会进行上一步的操作,并且将Eden和S0区的年纪大的对象放到S1区【原理上随时保持S0和S1有一个是空的,用来存下一次的对象】
  4. 下下次,Eden区快满的时候,会进行上一步操作,并且将Eden和S1区的年纪大的对象放到S0区【此时S1区就是空的】
  5. 直到Eden区快满,S0或者S1也快满的时候,这时候就把这两个区的年纪大的对象放到Old区
  6. 依次循环,直到Old区也快满的时候,Eden区也快满的时候,会对整个这一块内存区域进行一次大清洗(FullGC),腾出内存,为之后的对象创建,程序运行腾地方。

 

清理Eden区和Survivor区叫Minor GC;清理Old区叫Major GC;清理整个堆空间—包括年轻代和老年代叫Full GC。

  

4. JVM参数配置指南

前面三个部分对JVM进行了总体的了解,接下来是本文的重点。

-XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=256M -Xms256m -Xmx256m

  

文章看下来上面这段配置的意思很简单,设置元空间的初始值和最大值,设置堆空间的初始值和最大值。

为何MetaspaceSize要设置为128M?为何堆内存初始值Xms设置为256M而不是512M?

按照Java官方的指导
在这里插入图片描述

  • Java堆大小设置,Xms 和 Xmx设置为老年代存活对象的3-4倍,即FullGC以后的老年代内存占用的3-4倍
  • MaxPermSize(元空间)设置为老年代存活对象的1.2-1.5倍。
  • 年轻代Xmn的设置为老年代存活对象的1-1.5倍。
  • 老年代的内存大小设置为老年代存活对象的2-3倍。

可让系统运行一段时间后查看系统的各个指标,而后在进行配置。以下用jstat工具查看jvm的状况

jstat -gc 12345
###
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
13824.0 22528.0 13377.0  0.0   548864.0 535257.2  113152.0   46189.3   73984.0 71119.8 9728.0 9196.2     14    0.259   3      0.287    0.546

  OU表示老年代所占用的内存为 46189.3 K(大约45M);那么jvm相应的配置参数应该作以下修改

-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M -Xms180m -Xmx180m

  

posted @ 2022-03-05 18:24  咖啡not加糖  阅读(1)  评论(0)    收藏  举报