程序员的基本功:关于JVM

JVM

  • 堆的内存划分
    • minor gc(年轻)
      • Eden
        万物起源于eden神话,没有夏当与亚娃就没法有无限的可能​
      • Survivor
        • from
          被捉进监狱不断拷问,是不是在干活,干活的去另外一间,不干活的留下。当这个房间的所有干活的人都去到另外一个房间时,这房间中的所有人都会被干掉(干掉不干活)
        • to
          永远有一个房间是空的,用于放干活的人。​
    • major gc(专业/老人)
      • Tenured Gen(Old space)
      • Permgen space
        • PermGen Space:指的是内存的永久保存区,该块内存主要是被JVM用来存放 class 和 mete 信息的,当 class 被加载 loader 的时候就会被存储到该内存区中,与存放类的实例的heap区不同,java中的 垃圾回收器GC 不会在主程序运行期对 PermGen space 进行清理。
      • cms
        • 什么叫cms
          我的理解就是,CMS就是那个施展魔法的魔法师,他可以通过safepoint让大家都在同一时间停下来,然后开始他华丽的GC魔法。
          • CMS(Concurrent Mark-Sweep)(并发 标记-清除)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。
        • cms的生命周期
          • 初始标记(STW initial mark)
          • 并发标记(Concurrent marking)
          • 并发预清理(Concurrent precleaning)
          • 重新标记(STW remark)
          • 并发清理(Concurrent sweeping)
          • 并发重置(Concurrent reset)
  • GC相关
    • 什么频率的GC是不正常的?
      • 我认为FGC一天一次是不正常的
      • 我认为YGC频率是否正常,需要结合耗时和次数确认
      • 目前的GC频率监控
        • 10分钟内,老年代占比超过95%
        • 5分钟内,老年代占比超过99%
        • 3分钟内,GC总耗时大于20%(12秒)
        • 10分钟内,老年代占比超过95%
        • 3分钟内,GC总耗时大于20%(12秒)
        • 3分钟内,FGC次数大于3次
        • 5分钟内,老年代占比超过99%
        • 3分钟内,FullGC超过3次
    • young gc的影响?
      • Young GC 是全程 stop the world 的,时间可能有多方面原因决定
        • 各个线程到达安全点的等待时间;
        • 从 GC Root 扫描对象,进行标记的时间;
        • 存活对象 copy 到 Survivor 以及晋升 Old Gen 到的时间;
        • GC 日志的时间。
    • 什么情况下会晋升到老年代
      • 15次的划动
        人总有老的一天,被连接干了15次后,已经老了。要成为老人家了。对象在Survivor区中每熬过一次 Minor GC,年龄就增加1,当它的年龄增加到一定程度(默认为15)_时,就会被晋升到老年代中​
      • 一群人都老了
        有时候如果一群人都老了,都大于整个房间中的年纪的时候,可以直接提前成为老人家。例如:房间中有10个10岁的年轻小伙子和90个70岁的老太爷在工作,此时70岁的老太爷也是有可能会提前被放到老人区。​
      • 一出生就已经非常有经验,身体壮壮的,直接进老年代
    • GC魔法( 如何判断一个对象是否存活?(或者 GC 对象的判定方法))
      • 计算次数,例如A调用B,B的次数+1。引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收。
        引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A、B 对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。
      • 可达性算法(引用链法),该算法的思想是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用
        简单点说,就是看你上面还有没大BOSS,如果你没有BOSS只有一个人,那魔术师就认为你是个死缓的目标。
    • 什么叫safepoint
      • GC的时候必须要等到Java线程都进入到safepoint的时候VMThread才能开始执行GC,如果程序长时间运行而没有进入safepoint,那么GC也无法开始
      • safepoint time
        简单点的说,就是在GC前,有一个魔法师,他会拥有法术,可以让年轻世界中的所有人静止,并在他们身上打上是否正在工作。此时就是所谓的界面只有我一个,gc stop the word​
    • 怎么看YGC日志
      • YGC日志是长什么样的?
        • [GC (Allocation Failure) 2019-12-03T16:51:43.873+0800: 9446.668: [ParNew: 1679541K->1937K(1887488K), 0.0170647 secs] 2306110K->628563K(3984640K), 0.0174011 secs] [Times: user=0.17 sys=0.01, real=0.02 secs]
      • YGC日志中的重要信息
        • GC (Allocation Failure) 
          • Allocation Failure 代表什么?表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了
        • ParNew:前/后/总大小
          • 主要看年轻代中有多少空间,回收了多少
        • 堆前/后/总大小[年轻代+老年代]
          • 主要看整体
    • 怎么看FGC日志
      • FGC日志长什么样的?
        • [GC (CMS Initial Mark) [1 CMS-initial-mark: 630837K(2097152K)] 1589449K(3984640K), 0.0736613 secs] [Times: user=0.84 sys=0.10, real=0.08 secs]
        • [GC (CMS Final Remark) [YG occupancy: 1153884 K (1887488 K)]2019-12-03T16:44:45.153+0800: 8720.816: [Rescan (parallel) , 0.0886151 secs]2019-12-03T16:44:45.242+0800: 8720.904: [weak refs processing, 0.0064490 secs]2019-12-03T16:44:45.248+0800: 8720.911: [class unloading, 0.1078138 secs]2019-12-03T16:44:45.356+0800: 8721.019: [scrub symbol table, 0.0629573 secs]2019-12-03T16:44:45.419+0800: 8721.082: [scrub string table, 0.0041504 secs][1 CMS-remark: 630837K(2097152K)] 1784722K(3984640K), 0.2819051 secs] [Times: user=1.33 sys=0.13, real=0.28 secs]
      • FGC中的重要信息
        • 老年代由CMS收集器执行的Major GC相对比较复杂,包括初始标记、并发标记、重新标记和并发清除4个阶段
        • 初始标记阶段(CMS initial mark)
          • 这是CMS开始对老年代进行垃圾圾收集的初始标记阶段,该阶段从垃圾回收的“根对象”开始,且只扫描直接与“根对象”直接关联的对象,并做标记,需要暂停用户线程(Stop The Word,下面统称为STW),速度很快
            简单点说:就是魔法师开始进行魔法,大人物登场,都需要全界面关注,所以要stop the word
        • 并发标记阶段(CMS concurrent mark)
          魔法中
        • 重新标记阶段(CMS remark)
          魔法开始生效。前面搞了这么多魔法阵,现在开始施展魔法了,怎么地都要SHOW一下给全界面看一下,这时候就需要stop the word​[GC (CMS Final Remark)表示这是CMS的重新标记阶段,会STW,该阶段的任务是完成标记整个年老代的所有的存活对象,尽管先前的pre clean阶段尽量应对处理了并发运行时用户线程改变的对象应用的标记,但是不可能跟上对象改变的速度,只是为final remark阶段尽量减少了负担。[YG occupancy: 24305 K (39296 K)]表示年轻代当前的内存占用情况,通常Final Remark阶段要尽量运行在年轻代是足够干净的时候,这样可以消除紧接着的连续的几个STW阶段。[Rescan (parallel) , 0.0103714 secs]这是整个final remark阶段扫描对象的用时总计,该阶段会重新扫描CMS堆中剩余的对象,重新从“根对象”开始扫描,并且也会处理对象关联。本次扫描共耗时 0.0103714s。[weak refs processing, 0.0006267 secs]第一个子阶段,表示对弱引用的处理耗时为0.0006267s。[class unloading, 0.0368915 secs]第二个子阶段,表示卸载无用的类的耗时为0.0368915s。[scrub symbol table, 0.0486196 secs]最后一个子阶段,表示清理分别包含类级元数据和内部化字符串的符号和字符串表的耗时。[1 CMS-remark: 108093K(126116K)]表示经历了上面的阶段后老年代的内存使用情况。再后面的132398K(165412K), 0.1005635 secs表示final remark后整个堆的内存使用情况和整个final remark的耗时。作者:帅气滴糟老头链接:https://www.jianshu.com/p/ba768d8e9fec来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。​
    • 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?
      • 吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概 100M 左右的内存)就足够了。
  • 如何可视化查看GC
  • 如何使用工具查看GC
    • jmap
      • jstat -gc PID(查看GC情况)
      • jmap -heap PID(查看当前的堆情况)
      • jmap -histo:live PID(显示堆中对象的统计信息)
      • jmap -clstats(打印类加载器信息)
        • 打出来以后有什么用?
      • jcmd PID GC.status(一个新的诊断命令,用来连接到运行的JVM并输出详尽的类元数据的柱状图)
        • 但运行时JVM需要加上以下参数-XX:+UnlockDiagnosticVMOptions
      • jcmd 3426 GC.heap_dump test2.dump -all=true(使用JCMD生成堆快照)
      • jmap -dump:format=b,file=heapdump.hprof pid(生成堆转储快照dump文件)
  • JVM参数参考
    • -Xmx2048m-Xms2048m-Xloggc:/dev/shm/gc-osp-proxy-3080.log-XX:ReservedCodeCacheSize=96M-XX:ParallelGCThreads=8-XX:ParGCCardsPerStrideChunk=4096-XX:NewRatio=1-XX:MetaspaceSize=128m-XX:MaxTenuringThreshold=2-XX:MaxMetaspaceSize=256m-XX:MaxDirectMemorySize=2048m-XX:HeapDumpPath=/apps/logs/osp//-XX:ErrorFile=/apps/logs/osp//hs_err_%p.log-XX:CMSInitiatingOccupancyFraction=75-XX:AutoBoxCacheMax=20000-XX:-UseCounterDecay-XX:-UseBiasedLocking-XX:-TieredCompilation-XX:-OmitStackTraceInFastThrow-XX:-CMSClassUnloadingEnabled-XX:+UseConcMarkSweepGC-XX:+UseCMSInitiatingOccupancyOnly-XX:+UnlockDiagnosticVMOptions-XX:+PrintPromotionFailure-XX:+PrintGCDetails-XX:+PrintGCDateStamps-XX:+PrintGCApplicationStoppedTime-XX:+PrintCommandLineFlags-XX:+PerfDisableSharedMem-XX:+ParallelRefProcEnabled-XX:+HeapDumpOnOutOfMemoryError-XX:+ExplicitGCInvokesConcurrent-XX:+CMSParallelInitialMarkEnabled-XX:+AlwaysPreTouch-Dsun.rmi.transport.tcp.threadKeepAliveTime=75000 -Djava.rmi.server.hostname=127.0.0.1-Dstart.check.outfile=./status-Dosp.stub.max-connections-per-server=2-Djava.security.egd=file:/dev/./urandom-Djava.net.preferIPv4Stack=true-Djava.awt.headless=true-Dio.netty.recycler.maxCapacity.default=0-Dio.netty.leakDetectionLevel=disabled-Dcom.sun.management.jmxremote.ssl=false-Dcom.sun.management.jmxremote.port=3060-Dcom.sun.management.jmxremote.authenticate=false-Dcom.sun.management.jmxremote
      • NewRatio 就指新生代和老年代的比例,设置为1,即新生代(1):老年代(1),设置为4,即新生代(1):老年代(4)
      •  -XX:CMSInitiatingOccupancyFraction=70 是指设定CMS在对内存占用率达到70%的时候开始GC(因为CMS会有浮动垃圾,所以一般都较早启动GC);
      • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。
      • -XX:MaxTenuringThreshold=2 :熬过多少次Young GC后晋升到年老代
        这是改动效果最明显的一个参数了。对象在Survivor区最多熬过多少次Young GC后晋升到年老代,JDK8里CMS 默认是6,其他如G1是15。Young GC是最大的应用停顿来源,而新生代里GC后存活对象的多少又直接影响停顿的时间,所以如果清楚Young GC的执行频率和应用里大部分临时对象的最长生命周期,可以把它设的更短一点,让其实不是临时对象的新生代对象赶紧晋升到年老代,别呆着。用-XX:+PrintTenuringDistribution观察下,如果后面几代的大小总是差不多,证明过了某个年龄后的对象总能晋升到老生代,就可以把晋升阈值设小,比如JMeter里2就足够了。
      • ExplicitGCInvokesConcurrent :full gc时,使用CMS算法,不是全程停顿,必选。
      • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
  • 参考文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值