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




相关文章
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
55 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
28 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
26天前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
26 4
|
30天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
46 2
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
40 3
|
1月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
26 3
|
1月前
|
安全 Java API
🌟探索Java宇宙:深入理解Java技术体系与JVM的奥秘
本文深入探讨了Java技术体系的全貌,从Java语言的概述到其优点,再到Java技术体系的构成,以及JVM的角色。旨在帮助Java开发者全面了解Java生态,提升对Java技术的认知,从而在编程实践中更好地发挥Java的优势。关键词:Java, JVM, 技术体系, 编程语言, 跨平台, 内存管理。
34 2
|
1月前
|
Java Android开发 开发者
【编程进阶知识】精细调控:掌握Eclipse JVM参数配置的艺术
本文详细介绍了如何在Eclipse中配置JVM参数,包括内存的初始和最大值设置。通过具体步骤和截图演示,帮助开发者掌握JVM参数的精细调控,以适应不同的开发和测试需求。
41 1
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
51 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配