JVM技术实战-带你学习一下TomcatGC的配置

简介: JVM技术实战-带你学习一下TomcatGC的配置

垃圾回收时的停顿现象


垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以更高效的执行,大部分情况下,会要求系统进如一个停顿的状态


停顿的目的是为了终止所有的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一个瞬间的一致性,也有利于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿。





对象如何进入老年代


一般而言对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代那呢?


一般来说只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次GC决定的,在新生代每次GC后,如果没有被回收则年龄加1。


虚拟机提供一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代 -XX:MaxTenuringThreshold


public class Test5 {
    public static void main(String[] args) {
        //初始对象在eden区
        //-Xmx64m -Xms64m -XX:+PrintGCDetails
        /*for(int i=0;i<5;i++){
            byte[] b = new byte[1024*1024];
        }*/
        //测试进入老年代的对象
        //-Xmx1024m -Xms1024m -XX:+UseSerialGC -
        // XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails
        //-XX:+PrintHeapATGC
        for(int k=0;k<20;k++){
            for(int j=0;j<300;j++){
                byte[] b=new byte[1024*1024];
            }
        }
    }
}
复制代码


总结:根据设置MaxTenuringThreshold参数可以指定新生代对象经过多少次回收后进入老年代。另外,大对象(新生代eden区无法装入时,也会直接进入老年代)。JVM有个参数可以设置对象的大小超过指定的大小之后,直接晋升老年代。 -XX:PretenureSizeThreshold





TLAB(线程本地缓存)


TLAB全称是Thread Local Allocation Buffer即本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的


每一个线程会产生一个TLAB,该线程独享的一个工作区域,java虚拟机使用这种TLAB来避免多线程冲突问题。提高了对象分配的效率。TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配在堆上


-XX:+UseTLAB 使用TLAB。
-XX:+TLABSize设置TLAB大小。 
-XX:TLABRefillWasterFraction 设置维护进入TLAB空间的单个对象大小,它是一个比例值,默认64,即如果对象大于整个空间的1/64,则在堆创建对象。 
-XX:+PrintTLAB查看TLAB信息 
-XX:ResizeTLAB自调整TLABRefillWasteFraction阈值。 
复制代码


每个线程单独的一块工作内存(volatile)


public class Test7 {
    public static void alloc(){
        byte[] b = new byte[2];
    }
    public static void main(String[] args) {
        //TLAB分配
        //-XX:+UseTLAB -XX:+PrintTLAB -XX:TLABSize=102400 -
        XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis
        long start = System.currentTimeMillis();
        for(int i=0;i<10000000;i++){
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}
复制代码




垃圾收集器


在java虚拟机中,垃圾回收器不仅仅只有一种,什么情况下该使用哪种,对性能又有什么影响,这些都是我们需要了解的




串行垃圾收集器


串行回收器是指使用单线程进行垃圾回收的回收器。每次回收时,串行回收器只有一个工作线程,对于并行能力较弱的计算机来说,串行回收器的专注性和独占性往往有更好的性能表现 。串行回收器可以在新生代和老年代使用。根据作用于不同的对空间分为新生代串行回收器和老年代串行回收器。


-XX:+UseSerialGC参数可以设置使用新生代串行回收器和老年代串行回收器。



并行垃圾收集器


并行的垃圾回收器在串行回收器的基础上做了改进,他可以使用多个线程同时进行垃圾回收,对于计算能力强的计算机而言,可以有效的缩短垃圾回收所需的实际时间


ParNew回收器


  • 一个工作在新生代的垃圾回收器,他只是简单的将串行回收器多线程化,它的回收策略和算法和串行回收器一样。 使用-XX:+UseParNewGC 新生代使用ParNew回收器,老年代则使用串行回收器。


  • ParNew回收器工作时的线程数量可以使用-XX:ParallelGCThreads参数指定,一般最好和计算机的CPU相当,避免过多的线程影响性能。



新生代ParallelGC回收器


使用了复制算法的回收器,也是多线程独占形式的回收器,但ParallelGC回收器有一个很重要的特点,就是它非常关注吞吐量。


提供了两个参数控制系统的吞吐量


  • -XX: MaxGCPauseMillis:设置最大垃圾收集停顿时间,可用于把虚拟机在GC停顿的时间控制在MaxGCPauseMillis范围内,如果希望减少GC停顿时间可以将MaxGCPauseMillis设置的很小,但是会导致GC频繁,从而增加GC的总时间,降低了吞吐量。所以要根据实际情况设置该值。


  • -XX: GCTimeRatio:设置吞吐量的大小,它是一个0到100之间的整数,默认情况下它的取值是99,那么系统将花费不超过1/(1+n)的时间用于垃圾回收,也就是1/(1+99)=1%的时间。


  • 另外还可以指定-XX:+UseAdaptiveSizePolicy打开自适应模式,在这种模式下,新生代的大小、eden、from/to的比例,以及晋升老年代的对象的年龄参数将被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点



老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的ParallelGC回收器一样,也是一种关注吞吐量的回收器,它使用标记压缩算法实现。



  • -XX:+UseParallelOldGC进行设置


  • -XX:+ParallelGCThreads也可以设置垃圾收集时的线程数量。




CMS回收器


CMS全称为:Current Mark Sweep意为并发标记清除,他使用的是标记清除法,主要关注系统的停顿时间


  • 使用-XX:+UseConcMarkSweepGC进行设置


  • 使用-XX:+ConcGCThreads设置并发线程数量


CMS并不是独占的回收器,也就是说CMS回收的过程中,应用程序仍在不断的运行,又会有新的垃圾不断的产生,所以在使用CMS的过程中应该确保应用程序的内存足够用。CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阈值的时候开始回收,回收阈值可以用指定的参数进行配置,-XX:CMSInitiatingOccupancyFraction来指定,默认值为68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。


如果内存使用率增长很快,在CMS执行的过程中,已经出现内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这会使应用程序中断,直到垃圾回收完成后才会正常工作。这个过程GC停顿的时间可能比较长,所以-XX:CMSInitiatingOccupancyFraction的设置要根据实际情况。


之前我们在学习算法的时候说过,标记清除法有个缺点就是内存碎片的问题,那么CMS有个参数设置**-XX:+UseCMSCompactAtFullCollection可以使CMS回收完成之后进行一次碎片整理,-XX:CMSFullGCsBeforeCompaction**参数可以设置进行多少次CMS回收之后对内存进行一次压缩。




G1回收器


G1回收器(Garbage-First)是在jdk1.7中提出的垃圾回收器,从长期目标来看是为了取代CMS回收器,G1回收器拥有独特的垃圾回收策略,G1属于分代垃圾回收器,区分新生代和老年代,依然有eden和from/to区,并不要求整个eden区或新生代、老年代空间都连续,它使用的是分区算法


  • 并行性: G1回收期间可多线程同时工作


  • 并发性: G1拥有与应用程序交替执行的能力,部分工作可与应用程序同时执行,在整个GC期间不会完全阻塞应用程序


  • 分代GC: G1依然是一个分代收集器,但是它是兼顾新生代和老年代一起工作的,之前的垃圾收集器或者在新生代工作或者在老年代工作,因此这是一个很大的不同


  • 空间整理: G1在回收过程中,不会像CMS那样在若干次GC后进行碎片整理,G1采用有效复制对象的方式减少空间碎片。


  • 可预见性:由于分区的原因,G1可以只选取部分区域进行回收,缩小了回收的范围,提高了性能。


使用**-XX:+UseG1GC应用G1收集器 使用-XX:MaxGCPauseMillis指定最大的停顿时间 使用-XX:ParallelGCThreads**设置并行回收的线程数量


配置Tomcat参数(一)


使用串行垃圾回收器


-XX:+PrintGCDetails -Xmx32m -Xms32m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32m
复制代码


测试结果显示吞吐量为:871.8/sec 87KB/sec


配置Tomcat参数(二) 扩大内存提高系统性能


-XX:+PrintGCDetails -Xmx512m -Xms32m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32m
-Xloggc:d:/gc.log
复制代码


测试结果显示吞吐量为:1383.31/sec 138KB/sec



配置Tomcat参数(三)


调整初始堆大小


-XX:+PrintGCDetails -Xmx512m -Xms128m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32m
-Xloggc:d:/gc.log
复制代码


测试结果显示吞吐量为:1501.3/sec 149.8KB/sec



配置Tomcat参数(四) 测试ParNew回收器的表现


-XX:+PrintGCDetails -Xmx512m -Xms128m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParNewGC
-XX:PermSize=32m
-Xloggc:d:/gc.log
复制代码


测试结果显示吞吐量为:2130/sec 212KB/sec



配置Tomcat参数(五)


测试ParallelOldGC回收器的表现


-XX:+PrintGCDetails -Xmx512m -Xms128m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:ParallelGCThreads=8
-XX:PermSize=32M
-Xloggc:d:/gc.log
复制代码



测试结果显示吞吐量为:2236/sec 223KB/sec



配置Tomcat参数(五) 测试CMS回收器的表现


-XX:+PrintGCDetails -Xmx512m -Xms128m
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseConcMarkSweepGC
-XX:ConcGCThreads=8
-XX:PermSize=32M
-Xloggc:d:/gc.log
复制代码


测试结果显示吞吐量为:1813/sec 181KB/sec




目录
打赏
0
0
0
0
379
分享
相关文章
JVM实战—8.如何分析jstat统计来定位GC
本文详细介绍了使用jstat、jmap和jhat等工具分析JVM运行状况的方法,以及如何合理优化JVM性能。内容涵盖新生代与老年代对象增长速率、Young GC和Full GC的触发频率及耗时等关键指标的分析。通过模拟BI系统和计算系统的案例,展示了如何根据实际场景调整JVM参数以减少FGC频率,提升系统性能。最后汇总了常见问题及其解决方案,帮助开发者更好地理解和优化JVM运行状态。
JVM实战—8.如何分析jstat统计来定位GC
JVM实战—10.MAT的使用和JVM优化总结
本文详细探讨了JVM内存管理与性能优化的关键问题。首先分析了线上大促活动引发的老年代内存泄漏及频繁FGC问题,通过MAT工具定位到本地缓存未正确处理的原因,并提出使用Ehcache等框架解决。接着讨论了百万级数据误处理导致的频繁FGC案例,深入剖析String.split()方法在特定JDK版本下的内存消耗问题,并给出多线程并发处理大数据量的优化建议。文章还总结了JVM运行原理、GC机制以及YGC和FGC的触发条件,明确了正常系统GC频率指标。最后提供了JVM性能优化的整体思路,包括新系统开发时的参数预估、压测后的调整策略以及线上系统的监控方法,同时列举了常见的FGC原因及对应解决方案。
137 79
JVM实战—10.MAT的使用和JVM优化总结
JVM实战—11.OOM的原因和模拟以及案例
本文详细探讨了Java系统中内存溢出(OutOfMemory,简称OOM)问题的成因与解决方法。首先分析了线上系统因OOM挂掉的常见场景及处理思路,接着深入讲解了JVM中可能发生OOM的三大区域:Metaspace(类信息存储区)、栈内存(线程执行方法时使用)和堆内存(对象存储区)。针对每个区域,文章通过具体代码示例模拟了内存溢出的情况,如动态生成过多类导致Metaspace溢出、无限递归调用引发栈内存溢出以及高负载下堆内存不足等问题。最后结合实际案例,如大数据处理系统因Kafka故障未正确处理数据缓存而导致OOM,以及无限循环调用或未缓存动态代理类引发的问题,给出了预防和改进措施。
135 64
JVM实战—11.OOM的原因和模拟以及案例
JVM实战—7.如何模拟GC场景并阅读GC日志
本文主要介绍了:如何动手模拟出频繁Young GC的场景、JVM的Young GC日志应该怎么看、编写代码模拟动态年龄判定规则进入老年代、编写代码模拟S区放不下部分进入老年代、JVM的Full GC日志应该怎么看。
JVM实战—7.如何模拟GC场景并阅读GC日志
JVM实战—13.OOM的生产案例
本文详细探讨了多种线上系统中引发OOM(内存溢出)问题的原因及排查方法。内容涵盖:1)每秒仅上百请求的系统因RPC超时时间设置过长导致QPS激增而OOM;2)Jetty服务器NIO机制因堆外内存管理不当引发内存溢出;3)微服务架构下RPC调用因类定义不一致导致超大byte[]数组占用内存;4)SQL语句缺少WHERE条件查询大量数据引发OOM;5)日志分析系统因堆内存不足与递归操作耗尽内存;6)类加载器过多导致内存使用过高被OS杀死进程;7)数据同步系统频繁OOM的排查与解决;8)总结JVM参数优化、GC问题定位及OOM分析方法。
JVM实战—13.OOM的生产案例
JVM实战—9.线上FGC的几种案例
本文详细探讨了JVM性能优化中的几个关键案例与问题。首先分析了如何优化每秒十万QPS的社交APP,通过增加Survivor区大小和优化内存碎片解决频繁Full GC的问题。接着讨论了垂直电商后台系统FGC的深度优化,定制JVM参数模板以降低GC频率。还探讨了不合理设置JVM参数导致频繁FGC的情况,并提出了解决方案。此外,针对线上系统每天数十次FGC的问题,定位到大对象是主要原因,并通过调整新生代大小等参数优化。同时,分析了电商大促活动中因System.gc()调用导致系统卡死的现象,建议禁用显式GC。
JVM实战—9.线上FGC的几种案例
JVM实战—12.OOM的定位和解决
本文详细探讨了JVM内存管理中的常见问题及其解决方案,包括如何监控和报警系统的OOM异常、在内存溢出时自动Dump内存快照、解决Metaspace区域内存溢出、栈内存溢出(StackOverflowError)以及堆内存溢出(OutOfMemoryError: Java heap space)。针对每种情况,文章提供了具体的解决思路、示例代码、GC日志分析及内存快照分析方法。通过搭建系统监控体系、调整JVM参数和使用工具如MAT,可以有效定位和解决各类内存问题,优化系统性能并避免崩溃风险。
JVM实战—12.OOM的定位和解决
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
122 29
JVM简介—1.Java内存区域
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
676 166
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转