• Jvm优化


    Jvm优化参考

    version author date
    V1.0.0 2020-04-29

    目录

    Jvm优化参考... 1
    一、       预先期望描述... 2
    二、       基本概念理解... 3
    2.1     gc过程... 3
    2.2     gc范围... 3
    2.3     栈相关... 4
    2.4     堆相关... 4
    2.5     Slot复用... 4
    三、       图文描述... 5
    3.1     优化思维导图... 5
    3.2     jvm内存结构... 6
    3.3     堆内存分代结构... 8
    3.4     栈的结构模型... 9
    四、       性能优化实施... 10
    4.1     通过分配内存优化... 10
    4.2     选择垃圾回收器... 13
    4.3     减少gc开销... 14
    五、       要点摘录... 16
    5.1     内容出处... 16
    5.2     摘录内容... 16
    六、       常规典例图表... 19
    6.1     jvm参数配置项及描述... 19
    6.2     jvm常用配置列表... 19
    6.3     jvm内存配置典例参考指南... 20
    6.4     垃圾收集器选择典例参考指南... 20

    一、    预先期望描述

    预期:提高资源有效利用率
    描述:依据场景而定方案,减少时间/空间/运维成本、降低gc开销
    参考指标【待定】

    二、    基本概念理解

    2.1            gc过程

    【内容出处】

    http://ifeve.com/jvm-yong-generation/

    【简易表述】

    在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor
    GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

    2.2            gc范围

    在Java语言中,可作为GC Roots的对象包括下面几种:
      a) 虚拟机栈中引用的对象(栈帧中的本地变量表);
      b) 方法区中类静态属性引用的对象;
      c) 方法区中常量引用的对象;
      d) 本地方法栈中JNI(Native方法)引用的对象。

    2.3            栈相关

    未展开部分在【要点摘录】部分有补充,下同

    2.4            堆相关

    2.5            Slot复用

    三、    图文描述

    3.1            优化思维导图

    image.png
    图表 1【Jvm运行时数据区思维导图】

    3.2            jvm内存结构

    image.png
    图表 2【jvm内存结构(Ver>=jdk8)】
     
    image.png
    图表 3jvm内存结构(Ver>=jdk8)

    3.3            堆内存分代结构

    image.png
    图表 4【堆内存分代结构】

    3.4            栈的结构模型

    image.png
    图表 5【栈的结构模型】

    四、    性能优化实施

    4.1            通过分配内存优化

    【内容出处】

    https://zhidao.baidu.com/question/367248545489149932.html

    【参数配置列表与描述】


    -Xms:指定最小堆内存,
    描述:通常设置成跟最大堆内存一样,减少GC;

    -Xmx:指定java程序的最大堆内存
    描述:使用java
    -Xmx5000M -version判断当前系统能分配的最大堆内存;

    -Xmn:设置年轻代内存大小
    描述:整个堆大小=年轻代大小+年老代大小。所以增大年轻代后,将会减小年老代大小。此值对系统性能影响                  较大,Sun官方推荐配置为整个堆的3/8;

    -Xss:指定线程的最大栈空间,
    描述: 此参数决定了java函数调用的深度,值越大调用深度越深, 若值太小则容易出栈溢出错误                                             (StackOverflowError);

    -XX:PermSize:指定方法区(永久区)的初始值,
    描述:默认是物理内存的1/64,在Java8永久区移除, 代之的是元数据区,由-XX:MetaspaceSize指定;

    -XX:MaxPermSize:指定方法区的最大值
    描述:默认是物理内存的1/4,在java8中由-XX:MaxMetaspaceSize指定元数据区的大小;

    -XX:NewRatio=年老代与年轻代的比值
    描述:如 -XX:NewRatio=2, 表示年老代与年轻代的比值为2:1;

    -XX:SurvivorRatio= Eden区与Survivor区的from和to的大小比值
    描述:-XX:SurvivorRatio=8表示Eden区与Survivor区的大小比值是8:1:1,因为Survivor区有两个(from, to)。

    JVM事实上分为三大块,年轻代(YoungGen),年老代(Old Memory),及持久代(Perm,在Java8中被取消)。

    年轻代大小选择
    【响应时间优先的应用】
    尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。

    【吞吐量优先的应用】
    尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

    年老代大小选择
    【响应时间优先的应用】
    年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例。减少年轻代和年老代花费的GC时间,一般会提高应用的效率。

    【吞吐量优先的应用】
    一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
     
    较小堆引起的碎片问题
    因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

    -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。

    -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下这里设置多少次Full GC后,对年老代进行压缩。
    备注:详情见表

    4.2            选择垃圾回收器

    收集器设置
    -XX:+UseSerialGC:设置串行收集器
    -XX:+UseParallelGC:设置并行收集器
    -XX:+UseParalledlOldGC:设置并行老年代收集器
    -XX:+UseConcMarkSweepGC:设置并发收集器
    垃圾回收统计信息
    -XX:+PrintGC
    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps
    -Xloggc:filename
    并行收集器设置
    -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
    -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
    -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
    并发收集器设置
    -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
    -XX:ParallelGCThreads=n:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
    备注:详情见表

    4.3            减少gc开销

    【内容出处】

    https://www.cnblogs.com/zxf330301/p/5366404.html

    【理由】

    程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。

    【措施】

    (1)不要显式调用System.gc()

      此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

    (2)尽量减少临时对象的使用

      临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

    (3)对象不用时最好显式置为Null

      一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

    (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)

      由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

    (5)能用基本类型如Int,Long,就不用Integer,Long对象

      基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

    (6)尽量少用静态对象变量

    静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

    (7)分散对象创建或删除的时间

    集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

    五、    要点摘录

    5.1            内容出处

    https://blog.csdn.net/rongtaoup/article/details/89142396

    5.2            摘录内容

    1.       栈是线程私有的,他的生命周期与线程相同。每个线程都会分配一个栈的空间,即每个线程拥有独立的栈空间。
    2.       栈帧是栈的元素。每个方法在执行时都会创建一个栈帧。栈帧中存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中压栈到出栈的过程。
    3.       栈帧中,由一个局部变量表存储数据。局部变量表中存储了基本数据类型(boolean、byte、char、short、int、float、long、double)的局部变量(包括参数)、和对象的引用(String、数组、对象等),但是不存储对象的内容。局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小。
    4.       局部变量的容量以变量槽(Variable Slot)为最小单位,每个变量槽最大存储32位的数据类型。对于64位的数据类型(long、double),JVM 会为其分配两个连续的变量槽来存储。以下简称 Slot
    5.       JVM 通过索引定位的方式使用局部变量表,索引的范围从0开始至局部变量表中最大的 Slot 数量。普通方法与 static 方法在第 0 个槽位的存储有所不同。非 static 方法的第 0 个槽位存储方法所属对象实例的引用。
    6.       为了尽可能的节省栈帧空间,局部变量表中的 Slot 是可以复用的。方法中定义的局部变量,其作用域不一定会覆盖整个方法。当方法运行时,如果已经超出了某个变量的作用域,即变量失效了,那这个变量对应的 Slot 就可以交给其他变量使用,也就是所谓的** Slot 复用**。
    7.       再虚拟机的运行参数中加上“-verbose:gc”,这个参数的作用就是打印 GC 信息StackOverflowError:栈溢出错误
    8.       如果一个线程在计算时所需要用到栈大小 > 配置允许最大的栈大小,那么Java虚拟机将抛出 StackOverflowError
    9.       OutOfMemoryError:内存不足,栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
    如何设置栈参数?
    10.   使用 -Xss 设置栈大小,通常几百K就够用了。由于栈是线程私有的,线程数越多,占用栈空间越大。
    11.   栈决定了函数调用的深度。这也是慎用递归调用的原因。递归调用时,每次调用方法都会创建栈帧并压栈。当调用一定次数之后,所需栈的大小已经超过了虚拟机运行配置的最大栈参数,就会抛出 StackOverflowError 异常。
    12.   堆是Java虚拟机所管理的内存中最大的一块存储区域。堆内存被所有线程共享。主要存放使用new关键字创建的对象。所有对象实例以及数组都要在堆上分配。垃圾收集器就是根据GC算法,收集堆上对象所占用的内存空间(收集的是对象占用的空间而不是对象本身)。
    13.   年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。
    14.   老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。
    15.   注:Full GC是清理整个堆空间,包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。

    六、    常规典例图表

    6.1            jvm参数配置项及描述

    image.png

    6.2            jvm常用配置列表

    image.png

    6.3            jvm内存配置典例参考指南

    image.png

    6.4            垃圾收集器选择典例参考指南

    image.png
     
    需要源文件的朋友可以加我微信:erfsfj-wx
    doc:
    jvm调优典例
    jvm调优说明

  • 相关阅读:
    WPF编程系列
    使用ListBox控件来实现直方图控件(一)
    在WebBrowser控件中获取鼠标在网页上点击的位置
    使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果
    浮点数类型在计算机里面的表示方法
    点击asp:button按钮后,不刷新当前页面
    .NET ComponentArt 使用背景
    关于Microsoft ASP.NET 2.0 AJAX Extensions UpdatePanel 中使用 javascript 产生错误的问题
    基础平台数据导入(游标的使用)
    STUFF 函数的使用
  • 原文地址:https://www.cnblogs.com/erfsfj-dbc/p/12809934.html
Copyright © 2020-2023  润新知