JVM调优

 

目录

1.垃圾回收发生在哪里?

2.对象在什么时候可以被回收?

3.怎么判断对象是否可以被回收?

        1.引用计数算法:

        2.可达性分析算法:

3.如何回收这些对象?

4.GC算法:

        1.标记-清楚算法:

        2.复制算法:

        3.标记-整理算法:

        4.分代收集算法:

5,按系统线程执行方式可分为2种回收方式:

        1.串行收集:使用单线程处理垃圾回收

        2.并行收集:使用多线程处理垃圾回收,速度快,效率更高.

        3.并发收集:gc线程与用户线程同时工作,系统在垃圾回收时不需要暂停用户线程.

 6.查看应用的JVM配置:jmap -heap pid

7.JVM内存分配调优过程:

        1.vim catalina.sh

         2.未调优前,执行压测:

                 查看系统状态:发现CPU负载高

                查看JVM日志: 发现不断有GC操作

                将jvm日志导出,通过GCViewer打开分析:

        3.具体调优方法:

                调整JVM内存空间,减少FullGC次数.

                -Xms1g:堆初大小(理想值为机器物理内存的50%)

                -Xmx1g:堆最大值

        再次对年轻代gc优化:-Xmn512m

设置年轻代中Eden区,Survivor区的大小比例:

        -XX:SurvivorRatio=8,将Eden区与Survivor区的比例设为8:2.

 选择合适的GC回收器:

        提高响应时间:使用CMS回收器或者G1回收器.

        提高系统吞吐量:使用并发回收器.

        -XX:ParallelGCThreads=4--->最好与系统的CPU数量一致

JVM内存溢出分析:

         GC触发条件:

         常见的内存溢出类型:

 导致内存泄漏的问题:

        1.在并发情况下,没有做限流,短时间内创建了大量的对象而导致内存溢出.

        2.代码问题:由于内存泄漏而导致的内存溢出.

Tomcat中配置内存溢出日志信息:

 查看catalina.out发现已经提示内存溢出问题:

 查看压测应用的jvm使用情况:jmap -heap pid

 需要找到具体什么对象一直无法回收导致了内存泄漏:

jmap -histo pid

        jmap -histo pid >> jvmdump.log,---->vim jvmdump.og

 发现是cn.test.TestBean这个类创建太多对象导致内存泄漏,接下来就可以让研发去优化代码了.

jvisualvm和jconsole都是jdk自带的监控分析工具.


        面对不同的业务场景,垃圾回收的调优策略也是不一样的,比如;在堆内存比较高的情况下,需要调高对象的回收频率;而在CPU使用率比较高的情况下下,就需要想办法降低并发时GC的频率.

        但是所有的调优都是有目的,JVM内存分配调优也一样,在没有性能问题的时候,不要随意改变JVM内存分配的参数,有可能修改后反而会增加性能问题.

        JVM内存分配不合理最直接的表现就是频繁的GC,这会导致上下文切换等性能问题,从而降低了系统的吞吐量,增加系统的响应时间;当发现频繁的GC,但又是对象的正常创建和回收时候,就需要调整JVM内存分配了,从而减少因频繁GC带来的性能开销.

        当新创建一个对象时,对象会被分配到新生代的Eden区,并且JVM会给对象定义一个年龄计数器(通过参数:-XX:MaxTenuringThreshold设置).

        同时,也可能有另外一种情况,当Eden区空间不足时,虚拟机就会执行新生代的垃圾回收(Minor GC),这时JVM会把存活的对象转移到Survivor中,并给对象的年龄+1,对象在Survivor区中同样会经历Minor GC,每经历一次Minor GC,对象的年龄就会+1.

        内存空间的大小也是可以设置阈值的,可通过-XX:PetenureSizeThreshold设置直接分配到老年代区的最大内存空间,如果分配的对象和超过了内存阈值,对象就会被直接分配到老年代,这样做的好处就是减少了新生代的垃圾回收.

1.垃圾回收发生在哪里?

        GC发生在JVM内存区域中;而在JVM内存区域中,程序计数器,虚拟机栈和本地方法栈这3个区域都是现成私有不共享的,随着线程的创建而创建,销毁而销毁,栈中的栈帧随着方法的进入和退出进行入栈和出栈操作,每个栈帧中分配多少内存基本都是在类结构确定下来的时候就确定了的,因此这3个区域的内存分配和回收都具有确定性.

        因此,垃圾回收的重点是关注堆和方法区的内存;堆中的回收主要是对象的回收,方法区的回收主要是无用的类和废弃常量的回收.

2.对象在什么时候可以被回收?

        一个对象不再被引用,就代表这个对象可以被回收了.

3.怎么判断对象是否可以被回收?

        目前有2中算法可以判断对象是否可以被回收:

        1.引用计数算法:

                这种算法是通过一个对象的引用计数器来判断该对象是否还存在引用,每当对象被引用,引用计数器就会+1,每当引用失效,引用计数器就会-1,当对象的引用计数器的值为0时,就说明该对象不再被引用,可以被回收.

        2.可达性分析算法:

                GC roots是该算法的基础,GC roots是所有对象的根对象,JVM在加载时,会创建一些普通对象来引用正常对象,这些普通对象会作为正常对象的起始点,在垃圾回收时,会从这些GC roots开始向下搜索,当一个对象到GC roots之间没有任何引用连相连,就证明该对象是没有被引用的,目前HotSpot JVM采用的就是这种算法. 

        以上2中算法都是通过引用来判断对象是否可以被回收,引用方式又有以下4种:

                1.强引用:被强引用关联的对象永远不会被垃圾收集器回收掉.

                2.软引用:被软引用关联的对象,只有在系统将要发生内存溢出时,才会被回收掉.

                3.弱引用:被弱引用关联的对象,只要发生垃圾收集事件就会被回收掉.

                4.虚引用:被虚引用关联的对象的唯一作用就是在这个对象被回收时能收到一个系统通知.

3.如何回收这些对象?

        垃圾收集线程回收对象时,JVM垃圾回收遵循2个特性:

        1.自动性:java提供了一个系统级的线程来跟踪每一块被分配出去的内存,当JVM处于空闲状态时,GC线程会自动检测分配出去的内存空间,然后自动回收空闲的内存块.

        2.不可预期性:垃圾回收线程在JVM中是自动执行的,java程序是无法强制执行的,唯一能做的就是通过调用System.gc方法来"建议"GC线程来执行GC回收,但是否可执行,仍然是不可预期的.

4.GC算法:

        1.标记-清楚算法:

                分为"标记"和"清除"2个阶段.

                        优点:不需要移动对象,简单高效.

                        缺点:标记-清除过程效率低,GC时会产生内存碎片.

        2.复制算法:

                把内存空间分配为2个相等的区域,每次使用其中的一个区域,GC时遍历当前使用的的区域,把正在使用的对象复制到另外一块区域,此算法每次只处理正在使用中的对象.

                        优点:简单高效,不会产生内存碎片.

                        缺点:内存使用率低,有可能产生频繁复制问题.

        3.标记-整理算法:

                分为"标记"和"整理"2个阶段,首先标记哪些对象可以被回收,然后这些被标记的对象移动到另一端,然后直接清理掉边界以外的内存.

                        优点:综合了前2钟算法的优点.

                        缺点:仍需要移动局部对象.

        4.分代收集算法:

                基于对象生命周期划分为新生代,老年代,元空间.对不同生命周期的对象使用不同的算法进行回收.

                        优点:分区回收

                        缺点:对于长时间存活的对象,回收效果不明显.

5,按系统线程执行方式可分为2种回收方式:

        1.串行收集:使用单线程处理垃圾回收

                        优点:容易实现,效率高.

                        缺点:无法发挥多处理器的优势,需要暂停用户线程.

        2.并行收集:使用多线程处理垃圾回收,速度快,效率更高.

                        优点:CPU数越多,越能体现并行收集器的优势.

                        缺点:需要暂停用户线程.

        3.并发收集:gc线程与用户线程同时工作,系统在垃圾回收时不需要暂停用户线程.

 6.查看应用的JVM配置:jmap -heap pid

 GC收集器性能衡量指标:

        1.吞吐量:应用程序消耗时间/(应用程序消耗时间+GC消耗时间);即:应用程序所花时间和系统总

                        运行时间的比值.例如,系统运行了100分钟,GC耗时2分钟,则GC吞吐量为99%,

                        GC吞吐量要>95%.

        2.停顿时间:垃圾收集器正在运行时,应用程序暂停的时间.对于串行回收器来说,停顿的时间会更长,对于并发回收器来说,由于垃圾收集器和应用程序交替运行,程序的停顿时间就会变短,但其效率可能不如独占cpu的垃圾收集器,系统的吞吐量也可能会降低.

        3.垃圾回收频率:通常垃圾回收频率越低越好,增大堆内存空间可以有效降低GC发生的频率,但同时也意味着堆积的对象会越来越多,最终会增加回收的停顿时间.

7.JVM内存分配调优过程:

        1.vim catalina.sh

         2.未调优前,执行压测:

                 查看系统状态:发现CPU负载高

                查看JVM日志: 发现不断有GC操作

                将jvm日志导出,通过GCViewer打开分析:

 发现FullGC次数过多,且年轻代和老年代空间使用率均超过99%,而FullGC会导致stop-the-world的发生,从而严重影响服务器的性能,所以需要调整JVM中堆内存的大小,以减少FullGC的发生.

参考标准:

        GC频率:高频的FullGC会给系统带来非常大的性能消耗,虽然MinorGC相对于FullGC来说好了很多,但是过多的MinorGC仍然会给系统带来压力.

        内存:这里的内存是指堆内存(heap),堆内存又分为年轻代(新生代)和老年代,如果内存不足或分配不合理,会增加FullGC的次数,严重时也将导致CPU占用率达到90%以上,影响性能.

        吞吐量:频繁的FullGC会引起系统的上下文切换,增加系统的性能开销,从而影响线程的请求处理,最终导致系统的吞吐量下降.

        延时:JVM的GC持续时间也会影响到每次请求的响应时间.

        3.具体调优方法:

                调整JVM内存空间,减少FullGC次数.

                -Xms1g:堆初大小(理想值为机器物理内存的50%)

                -Xmx1g:堆最大值

 调整tomcat的堆内存后,复测,发现响应时间降低了,吞吐量升高了200.

 导出jvm日志,再次用GC Viewer打开分析

        发现Full GC次数从调优前的229次下降到2次.MinorGC从调优前的4486次下降到3565次.优化明显

 优化后的年轻代的GC次数仍然有3000+次,

        再次对年轻代gc优化:-Xmn512m

 扩充年轻代空间大小,减少Minor GC频率的好处?

        通常情况下,由于新生代空间较小,Eden区很快会被填满,这会导致频繁的Minor GC,因此通过增大新生代的空间大小来降低MinorGC的频率.

扩容Eden区可减少Minor GC次数,是否会增加单次MinorGC的时间?

        单次MinorGC由2部分组成:T1(扫描新生代)+T2(复制存活对象到Survivor区)

        假设一个对象在Eden区的存活时间是500ms,MinorGC的时间间隔是300ms,为增大新生代空间时,这个对象会被复制到Survivor区,会花费复制时间.此时MinorGC时间是T1+T2;

        增大Eden空间后,MinorGC的时间间隔可能就扩大到了600ms,此时这个存活500ms的对象在Eden区就会被回收,不会再复制,节省了复制时间,此时MinorGC时间是2T1.

        在虚拟机中,复制成本要远高于扫描成本.

增加年轻代空间后复测:  吞吐量上升了70左右,有一定效果,但是不明显.

此时使用GCViewer查看jvm日志,发现MinorGC次数由3500+下降至2900,但还是需要再次优化.

设置年轻代中Eden区,Survivor区的大小比例:

        -XX:SurvivorRatio=8,将Eden区与Survivor区的比例设为8:2.

 选择合适的GC回收器:

        提高响应时间:使用CMS回收器或者G1回收器.

        提高系统吞吐量:使用并发回收器.

        -XX:ParallelGCThreads=4--->最好与系统的CPU数量一致

 调优后再次复测

发现吞吐量又增加了100. 

JVM内存溢出分析:

         GC触发条件:

         常见的内存溢出类型:

 导致内存泄漏的问题:

        1.在并发情况下,没有做限流,短时间内创建了大量的对象而导致内存溢出.

        2.代码问题:由于内存泄漏而导致的内存溢出.

Tomcat中配置内存溢出日志信息:

 发起压力后查看服务器负载

发现cpu使用率高,MEM内存使用率超过60%,系统的MEM是4g,60%就是2.4G,我们分配给JVM的内存大小也才2G多一点,说明JVM内存已经填满,很可能发生了JVM内存溢出.

 查看catalina.out发现已经提示内存溢出问题:

java.long.outofmemoryError:java heap space

 查看压测应用的jvm使用情况:jmap -heap pid

 需要找到具体什么对象一直无法回收导致了内存泄漏:

jmap -histo pid

vim /etc/cron.daily/tmpwatch

 配置后再次执行jmap -histo pid,如果还是无法生成文件,则无法使用工具mat分析,此时使用命令:

        jmap -histo pid >> jvmdump.log,---->vim jvmdump.og

 发现是cn.test.TestBean这个类创建太多对象导致内存泄漏,接下来就可以让研发去优化代码了.

jvisualvm和jconsole都是jdk自带的监控分析工具.

jvisualVM分析工具使用

在jvisualvm需要连接的引用服务器上配置连接认证信息:

        配置好后重启tomcat服务.

jvisualvm:

jconsole分析工具使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chuntian_tester

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值