JVM日志参数十全大补丸

源码共读

共 6088字,需浏览 13分钟

 ·

2021-09-18 07:13

👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇


作者丨安琪拉的博客

来源丨安琪拉的博客


快吃下这颗JVM十全大补丸,妈妈再也不担心JVM日志看不懂了!

本文需要一些JVM的储备知识,如果对JVM内存区域还不熟悉,可以看看基础知识部分,在JVM群里提问,评论区留言都可以,安琪拉玩家都很热心,社区很随意。

实践JVM日志

我们先打印点GC日志实践一下,再开始讲后面的理论。

首先在IDEA 中设置打印GC的参数,比如我设置的参数如下,堆、新生代老年代都设置的比较小,这样比如容易出GC日志。

-Xms56m -Xmx56m -Xmn21m -Xss512k -XX:MetaspaceSize=12m -XX:MaxMetaspaceSize=12m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps

写一段普通的程序,for循环往Map里面塞东西,然后主动触发GC。

public class GCLogTest {

    private static Map<String, String> mapContainer = new HashMap<>();

    public static void main(String[] args) {

        String stringDataPrefix = "key_prefix_";

        for (int i = 0; i < 3000000; i++) {
            String newStringData = stringDataPrefix + i;
            mapContainer.put(newStringData, newStringData);
        }

        System.out.println("MAP size: " + mapContainer.size());
        System.gc(); // 明显的GC

        // 移除 2/3
        for (int i = 0; i < 2000000; i++) {
            String newStringData = stringDataPrefix + i;
            mapContainer.remove(newStringData);
        }

        System.out.println("MAP size: " + mapContainer.size());
        System.gc();
        System.out.println("End of program!");

    }
}

我们开始讲解GC日志,下面是完整的GC日志,我们分段讲解。

JVM问题场景

一般JVM平常大家不会去关注,一出问题往往还都是大问题,比如线上突然FGC徒增啦!下面讲讲常见的几种JVM问题:

  • 年轻代:young gc时间很长,比如十几秒,甚至几十秒,这种都是很不正常,一般是swap打开了。

  • 年轻代:young gc过于频繁,一般调大堆或者年轻代,或者把Eden区域和Survivor区域比重调一下能解决。

  • FullGC过于频繁一般是调大堆或者老年代能解决,当然前期是没有内存泄漏。

  • 内存碎片过多触发cms,可以在凌晨业务低峰的时候主动触发Full GC。

  • 程序本身代码有问题,导致内存泄露,或者gc严重,甚至oom,可以dump内存快照,自己通过MAT等工具分析,如果希望社区帮忙排查,丢到JVM群大家一起分析。

JVM参数大全

下面讲解一下常见的一些JVM 参数,吃下这颗十全大补丸。

整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小. JDK1.8以后没有持久代,而是换成了Metaspace(元数据),这块空间不属于堆。

常见的配置项先列出来:

-server  :  服务器模式,JVM有客户端和服务器二种模式,最主要的差别在于:-Server模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。JVM工作在Server模式下可以大大提高性能,Server模式下应用的启动速度会比client模式慢大概10%,但运行速度比Client VM要快至少有10倍。-client  : 客户端模式,与上面的对应

-Xms:初始堆大小,默认大小是物理内存的1/64(<1GB),默认剩下的堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,这个比例MinHeapFreeRatio参数可以调整。

-Xmx:最大堆大小,物理内存的1/4(<1GB),默认剩余堆内存大于70%时,JVM会缩小堆大小,直到 -Xms的最小限制这个比例可以通过MaxHeapFreeRatio参数调整。

-Xmn:年轻代大小(JDK >=1.4),Minor GC发生的地方。大小 = eden+ 2 survivor space。增大年轻代后, 将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-XX:NewSize 也是年轻代大小 (JDK < 1.4),跟Xmn功能一样。

-XX:PermSize :持久代初始值,JDK1.8 采用Metaspace(元数据),没有这玩意了,默认值是物理内存的1/64。

-XX:MaxPermSize : 持久代最大值, 默认物理内存的1/4。

-Xss:每个线程的栈大小,都知道线程调用局部变量都在栈中,函数调用就是压栈弹栈,JDK5.0 以后每个线程堆栈大小为1M, 以前每个线程堆栈大小为256K,应用一般根据自己的特性,进行调整,比如调用层次嵌套很深,可以设置大一点。在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右,一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。

-XX:NewRatio :年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代),-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5,一般大部分时候设置了 Xms=Xmx 并且设置了Xmn的情况下,该参数不需要进行设置,这个参数不那么需要被关注。

-XX:SurvivorRatio :Eden区与Survivor区的大小比值,设置为8, 则两个Survivor区与一个Eden区的比值为2:8, 一个Survivor区占整个年轻代的1/10。

-XX:+DisableExplicitGC :禁止System.gc(),也就是你在程序中写了System.gc()不会生效。

-XX:MaxTenuringThreshold :垃圾最大年龄,

-XX:+UseBiasedLocking  :可以设置是否开启锁的偏向,关于锁偏向,synchronized锁膨胀会有,可以看我之前写的 一个synchronized跟面试官扯了半个小时

-XX:PretenureSizeThreshold :对象超过多大是直接在旧生代分配

-XX:+CollectGen0First :FullGC时是否先YGC

收集器有关参数

  1. 并行收集器相关参数

-XX:+UseParallelGC :选择垃圾收集器为并行收集器,此配置仅对年轻代有效

-XX:+UseParNewGC :设置年轻代为并行收集,可与CMS收集同时使用,jdk1.8建议才用 ParNew + CMS

-XX:ParallelGCThreads : 并行收集器的线程数, 一般等于CPU核数比较合适

-XX:+UseParallelOldGC :年老代垃圾收集方式为并行收集(Parallel Compacting)

-XX:MaxGCPauseMillis :每次年轻代垃圾回收的最长时间(最大暂停时间)

-XX:GCTimeRatio :设置垃圾回收时间占程序运行时间的百分比

-XX:+ScavengeBeforeFullGC :Full GC前调用YGC

  1. CMS相关参数

-XX:+UseConcMarkSweepGC :使用CMS内存收集,如果用CMS,一般就用xmn参数设置年轻代,不用-XX:NewRatio等。注意最新的JVM版本,当使用-XX:+UseConcMarkSweepGC时,-XX:UseParNewGC会自动开启。因此,如果年轻代的并行GC不想开启,可以通过设置-XX:-UseParNewGC来关掉。

-XX:CMSFullGCsBeforeCompaction :多少次后进行内存压缩,由于并发收集器不对内存空间进行压缩, 整理, 所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理。

-XX+UseCMSCompactAtFullCollection :在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。增加这个参数是个好习惯。可能会影响性能, 但是可以消除碎片。

-XX:+UseCMSInitiatingOccupancyOnly :命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期,使用手动定义初始化,定义开始CMS收集,JVM通过CMSInitiatingOccupancyFraction的值进行每一次CMS收集,而不仅仅是第一次。

-XX:CMSInitiatingOccupancyFraction :默认为68,即当年老代的空间使用率达到68%时,会执行一次CMS回收。如果应用程序的内存使用率增长很快,可以根据应用特点,可以对该值进行调优,如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少年老代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发年老代串行收集器。

额外配置信息

-XX:+PrintGC  打印GC,输出形式:[GC 16384K->2544K(18944K), 0.0094143 secs] 、[Full GC 16384K->2544K(18944K), 0.0650971 secs]

-XX:+PrintGCDetails  打印详细GC,这个参数我们上面例子已经用过了。

-XX:+PrintGCTimeStamps  这个就是上面的格式打印GC的时间戳,格式参考:2021-04-01T23:35:41.454-0800

-XX:+PrintGCApplicationStoppedTime  打印垃圾回收期间程序暂停的时间

-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间

后续

昨天JVM群里有人贴出了一些GC日志截图,大家兴致很高,就写了这篇日志查看文给大家扫清看日志的障碍。

大家感兴趣可以加我微信:guofu-angela    ,JVM群学习群还有坑位。

现在微信推送机制改了,为了防止大家漏看每天的推送,建议星标一下安琪拉的博客。


-End-

最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!

点击👆卡片,关注后回复【面试题】即可获取

在看点这里好文分享给更多人↓↓

浏览 5
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报