• JVM基础知识和调优基础原理


    JVM原理

    什么是jvm

    java虚拟机,就是个应用程序,工作在用户态

    详解

    JVM是按照运行时数据的存储结构来划分内存结构的,JVM在运行java程序时,将它们划分成几种不同格式的数据,分别存储在不同的区域,这些数据统一称为运行时数据。运行时数据包括java程序本身的数据信息和JVM运行java需要的额外数据信息。

    JVM运行时数据区

    • 程序计数器–线程私有

      行号,指示程序执行到哪个位置

    • Java虚拟机栈–线程私有
    • 本地方法栈–线程私有

      操作系统底层的方法

    • Java堆–线程公用

    JVM内存分配

    栈内存分配 -xss 默认1M

    保存参数、局部变量、中间计算过程和其他数据。退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁。

    • 栈的优点:存取速度比堆块,仅次于寄存器,栈数据可以共享。
    • 栈的缺点:存在栈中的数据大小、生存期是在编译时就确定的,导致其缺乏灵活性。

      stack out of memory

      一般情况下不会溢出,方法不会写那么大

      堆内存分配:

      保存对象
    • 堆的优点:动态分配内存大小,生存期不必事先告诉编译器,它是在运行期动态分配的,垃圾回收器会自动收走不再使用的空间区域。
    • 堆的缺点:运行时动态分配内存,在分配和销毁时都要占用时间,因此堆的效率较低。

      堆结构:

    • Young:E区,S0,S1
    • Old:
    • Permanent:

    JVM堆配置参数:

    概述

    • -Xms 初始堆大小

      默认物理内存的1/64(<1GB)

    • -Xmx最大堆大小

      默认物理内存的1/4(<1GB),实际中建议不大于4GB

    • 一般建议设置 -Xms=-Xmx

      好处是避免每次在gc后,调整堆的大小,减少系统内存分配开销

    • 整个堆大小=年轻代大小+年老代大小+持久代大小

    新生代:

    • 新生代=1个eden区+2个survivor区
    • -Xmn 年轻代大小(1.4 or later)

      -XX:NewSize,-XX:MaxNewSize(设置年轻代大小,1.4之前)

    • -XX:NewRatio

      年轻代(包括E区和两个S区)与年老代的比值(除去持久代)
      一般情况下设置了Xms=Xmx并且设置了Xmn的情况下,该参数不需要设置。

    • -XX:ServivorRatio

      1个S区与E区大小的比值,默认设置为8,则1个S区占整个年轻代的1/10

    • 新生代用来存放JVM刚分配的Java对象

    老年代:

    • 老年代=整个堆-年轻代大小-持久代大小
    • 年轻代中经过垃圾回收没有回收掉的对象被复制到年老代
    • 老年代存储对象比年轻代年龄大的多,而且不乏大对象(缓存)
    • 新建的对象也有可能直接进入老年代
      • 大对象,可通过启动参数设置-XX:PretnureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配。
      • 大的数组对象,切数组中无引用外部对象
    • 老年代大小无配置参数

    持久代:

    • 持久代=整个堆-年轻代大小-老年代大小
    • -XX:PermSize -XX:MaxPermSize

      设置持久代的大小,一般情况推荐把-XX:PermSize设置成-XX:MaxPermSize的值为相同的值,因为持久代大小的调整也会导致堆内存需要触发fgc。

    • 存放Class、Method元信息,其大小与项目的规模、类、方法的数量有关。一般设置为128M就足够,设置原则是预留30%的空间。
    • 持久代的回收方式
      • 常量池中的常量,无用的类信息,常量的回收很简单,没有引用了就可以被回收
      • 对于无用的类进行回收,必须保证3点:
        • 类的所有实例都已经被回收
        • 加载类的ClassLoader已经被回收
        • 类对象Class对象没有被引用(即没有通过反射引用该类的地方)

    JVM内存垃圾回收:

    垃圾收集算法:

    • 引用计数算法(濒临被抛弃
    • 根搜索算法:

      从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明对象是不可用的。即不可达对象。
      在Java语言中,GC Roots包括:

    • 虚拟机栈中引用的对象。(大部分被回收的)
    • 方法区中静态属性实体引用的对象。
    • 方法区中常量引用的对象。
    • 本地方法栈中JNI引用的对象。

    垃圾回收算法:

    复制算法(Copying)

    当空间存活的对象比较少时,极为高效,此算法用于新生代内存回收,从E区回收到S0或S1

    标记清除算法(Mark-Sweep)

    产生碎片,适合老年代垃圾回收。

    标记整理压缩算法(Mark-Compac)

    稍慢,适合老年代垃圾回收,解决碎片问题,对象连续,成本更高

    名词解释:

    • 串行回收:gc单线程内存回收、会暂停所有用户线程,用于client端
    • 并行回收:收集是指多个GC线程并行工作,但此时用户线程是暂停的
    • 并发回收:是指用户线程与GC线程同时执行(不一定是并行,可能交替,但总体上是同时执行的),不需要停顿用户线程(其实CMS中用户线程还是需要停顿的,只是非常短,GC线程在另一个CPU上执行)

    JVM常见的垃圾回收器:

    Serial回收器(串行回收器)

    是一个单线程的收集器,只能使用一个CPU或者一条线程去完成垃圾收集,在进行垃圾收集时,必须暂停所有其他工作线程,直到收集完成

    • -XX:+UseSerialGC来开启(新生代和老年代都开启)
    • 使用复制算法(新生代)标记-压缩算法(老年代)
    • 串行的、独占式的垃圾回收器
    • 缺点:Stop-The-World

    ParNew回收器(并行回收器)

    也是独占式回收器,在收集过程中,应用程序全部暂停。如果是单CPU上或者并发能力较弱的系统上,还不如串行回收器性能好。

    • -XX:+UseParNewGC开启
    • -XX:ParallelGCThreads指定线程数,默认最好与CPU数量相当

    新生代Parallel Scavenge回收器

    吞吐量优先回收器

    • 关注CPU吞吐量,即运行用户代码的时间/总时间,适合运行后台运算
    • -XX:+UserParallelGC开启,这也是在Server模式下的默认值
    • -XX:GCTimeRatio
    • -XX:MaxGCPauseMillis

    老年代ParallelOld回收器

    • -XX:+UseParallelOldGC开启

    CMS(并发标记清除)回收器

    用的最广泛,标记和重新标记两个阶段仍然需要停止用户线程,但时间很快

    初始标记
    并发标记
    重新标记
    并发清除

    • 标记-清除算法:同时它又是一个使用多线程并发回收的垃圾收集器
    • -XX:ParallelCMSThreads:手工设定CMS线程数量,CMS默认启动的线程数是(ParallelGCThreads+3)/4
    • -XX:+UseConcMarkSweepGC开启
    • -XX:CMSInitialtingOccupancyFraction
      设置CMS收集器在老年代空间被使用多少后触发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70
    • -XX:+UseCMSCompactAtFullCollection
      由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效。
    • -XX:+CMSFullGCBeforeCompaction
      设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用
    • -XX:CMSInitiatingPermOccupancyFraction
      设置持久代

    GC性能指标

    吞吐量

    应用花在非GC上的时间百分比

    GC负荷

    花在GC时间百分比

    暂停时间(看GClog)

    应用划在GC stop-the-world的时间

    GC频率

    反应速度

    从一个对象变成垃圾到这个对象被回收的时间

    小结

    • 一个交互式的应用要求暂停时间越少越好,然而,一个非交互式的应用,希望GC负荷越低越好
    • 一个实时系统对暂停时间和GC负荷要求,都是越低越好

    内存容量配置原则

    年轻代大小选择

    • 响应时间优先的应用

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

    • 吞吐量优先的应用

      尽可能设置大,可能到达Gbit的程度,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用
      避免设置过小,当新生代设置过小时会导致

    • YGC次数更加频繁
    • 可能导致YGC对象直接进入老年代,如果此时老年代满了,会触发FGC

    老年代大小选择

    • 响应时间优先的应用

      使用并发垃圾收集器(CMS)设置小了会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式,如果堆大了,需要较长的收集时间,最优化的方案,一般参考以下数据获得:
      并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例

    • 吞吐量优先的应用

      一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽量存放长期存活对象。

    java排障

    使用jps获取java进程的pid

    1
    # jps -lvm

    导出CPU占用高进程的线程栈

    1
    jstack `$pid` >> java.txt

    查看对应进程的哪个线程占用CPU过高

    1
    # top -H -p 22056

    将线程的pid转换为16进制

    1
    # echo "obase=16;`$pid`"|bc

    第二步中导出的java.txt中查找转换为16进制的线程pid,找到对应的线程栈

    分析负载高的线程栈都是什么业务操作,优化程序并处理问题

    原文地址:https://blog.stanley.wang/page/2/

  • 相关阅读:
    rsyslog日志服务器搭建
    使用原生js实现对table中的某个单元格进行编辑并提交后台修改数据
    使用vuex管理的状态数据在刷新页面后数据丢失的问题
    brew install mongodb
    brew安装与启动redis
    zsh设置代理
    DNS Rebinding漏洞原理
    防数据泄露_MySQL库和数据安全
    PHP代码审计_用==与===的区别
    MySQL提权 通过UDF
  • 原文地址:https://www.cnblogs.com/lanbojini/p/14304407.html
Copyright © 2020-2023  润新知