天道酬勤,学无止境

JVM层GC调优(上)

JVM内存结构简介(jdk1.8)

JVM层的GC调优是生产环境上必不可少的一个环节,因为我们需要确定这个进程可以占用多少内存,以及设定一些参数的阀值。以此来优化项目的性能和提高可用性,而且这也是在面试中经常会被问到的问题。

想要进行GC调优,我们首先需要简单了解下JVM的内存结构,Java虚拟机的规范文档如下:

https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

在介绍JVM内存结构之前,我们需要先知道运行时数据区这样的一个东西,它与JVM的内存结构有着一定的关联。不过它属于是一个规范,所以与JVM内存结构是有着物理上的区别的。运行时数据区如下:
JVM层GC调优(上)

1.程序计数器(Program Count Register,简称PC Register):

  • JVM支持多线程同时执行,每一个线程都有自己的PC Register。当每一个新线程被创建时,它都将得到它自己的PC Register。线程正在执行的方法叫做当前方法。如果执行的是Java方法,那么PC Register里存放的就是当前正在执行的指令的地址,如果是native方法(C/C++编写的方法),则是为空。此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2.虚拟机栈(JVM Stacks):

  • Java虚拟机栈(Java Virtual Machine Stacks)是线程私有的,它的生命周期与线程相同。虚拟机描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程,实际上就是所谓的线程堆栈。
  • 局部变量表存放了各种基本类型、对象引用和returnAddress类型(指向了一条字节码指令地址)。其中64位长度 long 和 double 占两个局部变量空间,其他只占一个。
  • 该区域中规定的异常情况有两种:1.线程请求的栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;2.如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就抛出OutOfMemoryError异常。

3.堆Heap:

  • Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
  • Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。堆中可细分为新生代和老年代,再细分可分为Eden空间、From Survivor空间、To Survivor空间。堆无法扩展时,会抛出OutOfMemoryError异常。

4.方法区(Method Area):

  • 方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来。
  • 当方法区无法满足内存分配需求时,抛出OutOfMemoryError

5.运行时常量池(Run-Time Constant Pool):

  • 如上图所描述的一样,它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项是常量池(Const Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后被放入方法区的运行时常量池中存储。并非预置入Class文件中常量池的内容才进入方法运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
  • 同样的,当方法区无法满足内存分配需求时,也会抛出OutOfMemoryError

6.本地方法栈(Native Method Stacks):

  • 本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

了解了运行时方法区规范后,我们接下来看看JVM的内存结构图:
JVM层GC调优(上)

如上图,可以看到JVM内存被分为了两大区,非堆区用于存储对象以外的数据:

  • Metaspace:存放Class、Package、Method、Field、字节码、常量池、符号引用等等
    • CCS:这个区域存放32位指针的Class,也就是压缩类空间,默认关闭,需要使用JVM参数开启
    • CodeCache:存放JIT编译后的本地代码以及JNI使用的C/C++代码

而堆区则用于存储对象相关数据:

  • Young:新生代,存放新的或只经过几次Minor GC的对象
    • Eden:存放最新创建的对象,一些较大的对象则会特殊处理
    • S0/S1:当对象经过第一次Minor GC后,如果仍然存活,就会存放到这里。需要注意的是,S0和S1区域在同一时间上,只有其中一个是有数据的,而另一个则是空的。
  • Old:老年代,当S0或S1区域存满对象了,就会把这些对象存放到这个old区域中

在图中也可以看到,堆区还被分为了年轻代(young)和老年代(old)。那么为什么会有年轻代:

我们先来捋一捋,为什么需要把堆区分代?不分代不能完成它所做的事情么?其实不分代也完全可以,分代的唯一理由就是优化GC性能。你先想想,如果没有分代,那我们所有的对象都会存在同一个空间里。当进行GC的时候,我们就要找到哪些对象是没有用的,这样一来就需要对整个堆区进行扫描。而我们的很多对象都是只存活一瞬间的,所以GC就会比较频繁,而每次GC都得扫描整个堆区,就会导致性能低下。不进行GC的话,又会导致内存空间很快被占满。

因为GC性能的原因,所以我们才需要对堆区进行分代。如果进行分代的话,我们就可以把新创建的对象专门存放到一个单独的区域中,当进行GC的时候就优先把这块存放“短命”对象的区域进行回收,这样就会腾出很大的空间出来,并且由于不用去扫描整个堆区,也能极大提高GC的性能。

年轻代中的GC:

从上图中也可以看到年轻代被分为了三部分:1个Eden区和2个Survivor区,一般我们都会简称为S0、S1(同时它们还分为from和to两种角色),默认比例为8:1。一般情况下,最新创建的对象都会被分配到Eden区(一些大对象会特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是"短命"的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。所以才会有S0和S1区,复制算法的优点就是吞吐量高、可实现高速分配并且不会产生内存碎片,所以才适用于作为年轻代的GC算法。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

JVM中的对象分配:

  • 对象优先在Eden区分配
  • 大对象则会直接进入老年代

我们了解完JVM内存结构后,再来看看一些常用的JVM参数:

1.设置年轻代的大小,和年轻代的最大值,具体的值需要根据实际业务场景进行判断。如果存在大量临时对象就可以设置大一些,否则小一些,一般为整个堆大小的1/3或者1/4。为了防止年轻代的堆收缩,两个参数的值需设为一样大:

  • -XX:NewSize
  • -XX:MaxNewSize

2.设置Metaspace的大小,和Metaspace的最大值,同样需设为一样大:

  • -XX:MetaspaceSize
  • -XX:MaxMetaspaceSize

3.设置Eden和其中一个Survivor的比例,这个值也比较重要:

  • -XX:SurvivorRatio

4.设置young和old区的比例:

  • -XX:NewRatio

5.这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小:

  • -XX:+PrintTenuringDistribution

6.用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1:

  • -XX:InitialTenuringThreshol
  • -XX:MaxTenuringThreshold

7.使用短直针,也就是启用压缩类空间(CCS):

  • -XX:+UseCompressedClassPointers

8.设置CCS空间的大小,默认是一个G:

  • -XX:CompressedClassSpaceSize

9.设置CodeCache的一个初始大小:

  • -XX:InitialCodeCacheSize

10.设置CodeCache的最大值:

  • -XX:ReservedCodeCacheSize

11.设置多大的对象会被直接放进老年代:

  • -XX:PretenureSizeThreshold

12.长期存活的对象会被放入Old区,使用以下参数设置就可以设置对象的最大存活年龄:

  • -XX:MaxTenuringThreshold

注:如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论,linux64的java6默认值是15:

13.设置Young区每发生GC的时候,就打印有效的对象的岁数情况:

  • -XX:+PrintTenuringDistribution

14.设置Survivor区发生GC后对象所存活的比例值:

  • -XX:TargetSurvivorRatio

常见垃圾回收算法

本小节我们来简单介绍一些常见的垃圾回收算法,众所周知Java区别与C/C++的一点就是,Java是可以自动进行垃圾回收的。所以在Java中的内存泄露概念和C/C++中的内存泄露概念不一样。在Java中,一个对象的指针一直被应用程序所持有得不到释放就属于是内存泄露。而C/C++则是把对象指针给弄丢了,该对象就永远无法得到释放,这就是C/C++里的内存泄露。

在进行垃圾回收的是时候,要如何确认一个对象是否是垃圾呢?在很久以前有一种方式就是使用引用计数,当一个对象指针被其他对象所引用时就会进行一个计数。在进行垃圾回收时,只要这个计数存在,那么就会判断该对象就是存活的。而没有引用计数的对象,就会被判断为垃圾,可以进行回收。但是这种方法缺陷很明显,计数会占用资源不说,如果当一个A对象和一个B对象互相持有对方引用时,那么这两个对象的引用计数都不会为0,就永远不会被回收掉,这样就会导致内存泄露的问题。

在Java中,则是采用枚举根节点的方式:

  • 思想:枚举根节点,做可达性分析
  • 根节点:可以是类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等等

JVM层GC调优(上)

如上图,JVM会从根节点开始遍历引用,只要顺着引用路线所遍历到的对象都会判断为存活对象,即是具有可达性的,这些对象就不会被回收。而没有被遍历到的对象,也就是图中的E和F对象,即便它们俩互相都还存在引用,也会被回收掉,因为它们不存在根节点的引用路线中,即是不具有可达性的。


既然了解了JVM如何判断一个对象是否为垃圾后,我们就可以来看看一些垃圾回收算法了:

1.标记-清除:

  • 算法:该算法分为“标记” 和 “清除” 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一进行回收
  • 缺点:效率不高,标记和清除两个过程的效率都不高。容易产生内存碎片,碎片太多就会导致提前GC。

2.复制算法:

  • 算法:它将可用内存按容量划分为大小相等的两个块,每次只使用其中的一块。当这一块内存用完了,就将还存活的对象复制到另一个块上,然后再把已使用过的内存空间一次清理掉。
  • 优缺点:实现简单,运行高效,吞吐量大,但是空间利用率低,一次只能利用50%

3.标记-整理:

  • 算法:标记过程仍然与 “标记-清除” 算法一样,当后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
  • 优缺点:没有了内存碎片,但是整理内存比较耗时

4.分代垃圾回收:

  • 算法:这就是目前JVM所使用的垃圾回收算法,可以看到以上所介绍到的算法都各自有优缺点。而JVM就是把这些算法都整合了起来,在不同的区域使用不同的垃圾回收算法。Young区使用复制算法,Old区则使用标记清除或者标记整理算法。

垃圾收集器

在上一小节了解了一些常见的垃圾回收算法后,我们再来看看JVM中常见的垃圾收集器:

  • 1.串行收集器Serial:Serial、Serial Old
  • 2.并行收集器Parallel:Parallel Scavenge、Parallel Old,吞吐量优先,是Server模式下的默认收集器。默认在内存大于2G,CPU核心数大于2核的环境下为Server模式
  • 3.并发收集器Concurrent:CMS、G1,停顿时间优先

注:串行收集器几乎不会在web应用中使用,所以主要介绍并行和并发收集器

串行 VS 并行 VS 并发:

  • 串行(Serial):指只有单个垃圾收集线程进行工作,也就是单线程的,当垃圾收集线程启动的时候,用户线程会处于一个等待状态。适合内存较小的嵌入式开发中
  • 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。适合科学计算、后台处理等弱交互场景
  • 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),垃圾收集线程在执行的时候不会停顿用户程序的运行。适合对响应时间有要求、交互性强的场景,比如Web开发

停顿时间 VS 吞吐量:

  • 停顿时间:指垃圾收集器在进行垃圾回收时所中断应用执行的时间。可以使用以下参数进行设置:
    • -XX:MaxGCPauseMillis
  • 吞吐量:指花在垃圾收集的时间和花在应用时间的占比。可以使用以下参数进行设置:
    • -XX:GCTimeRatio=< n > 垃圾收集时间占:1/1+n

开启串行收集器:

  • -XX:+UseSerialGC(Young区)
  • -XX:+UseSerialOldGC(Old区)

开启并行收集器:

  • -XX:+UseParallelGC(Young区)
  • -XX:+UseParallelOldGC(Old区)
  • -XX:ParallelGCThread=< N > 设置N个GC线程,N取决于CPU核心数

并发收集器在JDK1.8里有两个,一个是CMS,CMS因为具有响应时间优先的特点,所以是低延迟、低停顿的,CMS是老年代收集器。开启该收集器的参数如下:

  • -XX:+UseParNewGC(Young区)
  • -XX:+UseConcMarkSweepGC(Old区)

另一个是G1,开启该收集器的参数如下:

  • -XX:+UseG1GC(Young区、Old区)

垃圾收集器搭配图:
JVM层GC调优(上)

注:实线代表可搭配使用的,虚线表示当内存分配失败的时候CMS会退化成SerialOld。JDK1.8中建议使用的是G1收集器

有这么多的垃圾收集器,那么我们要如何去选择合适的垃圾收集器呢?这个是没有具体答案的,都得按照实际的场景进行选择,但一般都会按照以下原则来进行选择:

  • 优先调整堆的大小让服务器自己来选择
  • 如果内存小于100M,使用串行收集器
  • 如何是单核,并且没有停顿时间的要求,就可以使用串行或由JVM自己选择
  • 如果允许停顿时间超过1秒,选择并行或者JVM自己选择
  • 如果响应时间最重要,并且不能超过1秒,则使用并发收集器

其中并行收集器是支持自适应的,通过设置以下几个参数,并行收集器会以停顿时间优先去动态调整参数:

  • -XX:MaxGCPauseMillis=< N >
  • -XX:GCTimeRatio=< N >
  • -Xmx< N >

当内存不够的时候并行收集器可以动态调整内存,虽然实际生产环境中用的比较少,至于每次动态调整多少内存,则使用以下参数进行设置:

  • -XX:YoungGenerationSizeIncrement=< Y > (增加,Young区,默认20%)
  • -XX:TenuredGenerationSizeIncrement=< T > (增加,Old区,默认20%)
  • -XX:AdaptiveSizeDecrementScaleFactor=< D >(减少,默认4%)

了解了并行收集器后,我们来简单看看CMS收集器其他的一些特性以及相关调优参数。

CMS垃圾收集过程:

  • 1.CMS initial mark:初识标记Root,STW
  • 2.CMS concurrent mark:并发标记
  • 3.CMS-concurrent-preclean:并发预清理
  • 4.CMS remark:重新标记,STW
  • 5.CMS concurrent sweep:并发清除
  • 6.CMS concurrent-reset:并发重置

CMS的缺点:

  • CPU敏感
  • 会产生浮动垃圾
  • 会产生空间碎片

CMS的相关调优参数:

设置并发的GC线程数:

  • -XX:ConcGCThreads

开启以下参数可以在Full GC之后对内存进行一个压缩,以此减少空间碎片:

  • -XX:+UseCMSCompactAtFullCollection

这个参数则是设置多少次Full GC之后才进行压缩:

  • -XX:CMSFullGCsBeforeCompaction

设置Old区存满多少对象的时候触发Full GC,默认值为92%:

  • -XX:CMSInitiatingOccupancyFraction

启用该参数表示不可动态调整以上参数的值:

  • -XX:+UseCMSInitiatingOccupancyOnly

启用该参数表示在Full GC之前先做Young GC:

  • -XX:+CMSScavengeBeforeRemark

在jdk1.7之前可以使用以下参数,启用回收Perm区:

  • -XX:+CMSClassUnloadingEnable

在jdk1.8后,推荐使用的垃圾收集器是G1。G1收集器在jdk1.7中第一次出现,所以到了jdk1.8里就非常成熟了。

G1收集器官网介绍如下:

The Garbage-First (G1) garbage collector is fully supported in Oracle JDK 7 update 4 and later releases. The G1 collector is a server-style garbage collector, targeted for multi-processor machines with large memories. It meets garbage collection (GC) pause time goals with high probability, while achieving high throughput. Whole-heap operations, such as global marking, are performed concurrently with the application threads. This prevents interruptions proportional to heap or live-data size.

The first focus of G1 is to provide a solution for users running applications that require large heaps with limited GC latency. This means heap sizes of around 6GB or larger, and stable and predictable pause time below 0.5 seconds.

官方文档地址:

http://www.oracle.com/technetwork/java/javase/tech/g1-intro-jsp-135488.html

原理概述:

G1 也是属于分代收集器的,但是G1的分代是逻辑上的,而不是物理上的

G1 将整个对区域划分为若干个Region,每个Region的大小是2的倍数(1M,2M,4M,8M,16M,32M,通过设置堆的大小和Region数量计算得出。

Region区域划分与其他收集类似,不同的是单独将大对象分配到了单独的region中,会分配一组连续的Region区域(Humongous start 和 humonous Contoinue 组成),所以一共有四类Region(Eden,Survior,Humongous和Old),G1 作用于整个堆内存区域,设计的目的就是减少Full GC的产生。在Full GC过程中由于G1 是单线程进行,会产生较长时间的停顿。

G1的OldGc标记过程可以和yongGc并行执行,但是OldGc一定在YongGc之后执行,即MixedGc在yongGC之后执行。

结构图:
JVM层GC调优(上)

G1垃圾收集算法主要应用在多CPU大内存的服务中,在满足高吞吐量的同时,尽可能的满足垃圾回收时的暂停时间,该设计主要针对如下应用场景:

  • 垃圾收集线程和应用线程并发执行,和CMS一样
  • 空闲内存压缩时避免冗长的暂停时间
  • 应用需要更多可预测的GC暂停时间
  • 不希望牺牲太多的吞吐性能

G1的几个概念:

  • Region:G1收集器所划分的内存区域
  • SATB:Snapshot-At-TheBeginning,它是通过Root Tracing得到的,GC开始时候存活对象的快照
  • RSet:记录了其他Region中的对象,引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)

G1中的Young GC过程,和以往的是一样的:

  • 新对象进入Eden区
  • 存活对象拷贝到Survivor区
  • 存活时间达到年龄阈值时,对象晋升到Old区

但是G1中没有Full GC,取而代之的是Mixed GC:

  • 它不是Full GC,所以触发Mixed GC时回收的是所有的Young区和部分Old区的垃圾

G1里还有一个概念叫全局并发标记(global concurrent marking),和CMS的并发标记是类似的:

  • 1.Initial marking phase:标记GC Root,STW
  • 2.Root region scanning phase:根区扫描
  • 3.Concurrent marking phase:并发标记存活对象
  • 4.Remark phase:重新标记,STW
  • Cleanup phase:部分STW

G1相关调优参数:

设置堆占有率达到这个参数值则触发global concurrent marking,默认值为45%:

  • -XX:InitiatingHeapOccupancyPercent

设置在global concurrent marking结束之后,可以知道Region里有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数的值,只有达到了,下次才会发生Mixed GC:

  • -XX:G1HeapWastePercent

设置Old区的Region被回收时的存活对象占比:

  • -XX:G1MixedGCLiveThresholdPercent

设置一次global concurrent marking之后,最多执行Mixed GC的次数:

  • -XX:G1MixedGCCountTarget

设置一次Mixed GC中能被选入CSet的最多Old区的Region数量:

  • -XX:G1OldCSetRegionThresholdPercent

其他参数:

  • -XX:+UseG1GC //开启G1收集器
  • -XX:G1HeapRegionSize=n //设置Region的大小,大小范围:1-32M,数量上限:2048个
  • -XX:MaxGCPauseMillis=200 //设置最大停顿时间
  • -XX:G1NewSizePercent //设置Young区大小
  • -XX:G1MaxNewSizePercent //设置Young区最大占整个Java Heap的大小,默认值为60%
  • -XX:G1ReservePercent=10 //保留防止to space溢出
  • -XX:ParallelGCThreads=n //设置SWT线程数
  • -XX:ConcGCThreads=n //并发线程数=1/4*并行

注意事项:

  • 年轻代大小:避免使用-Xmn、-XX:NewRatio等显式设置Young区大小,会覆盖暂停时间目标
  • 暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量

至于是否需要切换到G1收集器,可以根据以下原则进行选择:

  • 50%以上的堆被存活对象占用
  • 对象分配和晋升的速度变化非常大
  • 垃圾回收时间特别长,超过了1秒

关于在Web应用中,如何判断一个垃圾收集器的好坏,主要是看以下两点,以下两点都需为优才是好的垃圾收集器:

  • 1.响应时间
  • 2.吞吐量

下一篇:

  • JVM层GC调优(下)

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • JVM性能调优的6大步骤,及关键调优参数详解
    一、JVM内存调优 对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。 1.Full GC 会对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比较慢,因此应该尽可能减少Full GC的次数。 2.导致Full GC的原因 1)年老代(Tenured)被写满 调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 。 2)持久代Pemanet Generation空间不足 增大Perm Gen空间,避免太多静态对象 , 控制好新生代和旧生代的比例 3)System.gc()被显示调用 垃圾回收不要手动触发,尽量依靠JVM自身的机制 在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节,下面详细介绍对应JVM调优的方法和步骤。 二、JVM性能调优方法和步骤 1.监控GC的状态 使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化。 举一个例子: 系统崩溃前的一些现象: 每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5sFullGC的次数越来越多
  • Java后端进阶-JVM参数调优
    package com.study.performance; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; // 启动程序,模拟用户请求 // 每100毫秒钟创建1000线程,每个线程创建一个512kb的对象,最多100毫秒内同时存在1000线程,并发量1000/s,吞吐量6000/s,查看GC的情况 @SpringBootApplication public class PerformanceApplication { public static void main(String[] args) { SpringApplication.run(PerformanceApplication.class, args); Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { new Thread(() -> { for
  • java jvm 参数 -Xms -Xmx -Xmn -Xss 调优总结
    常见配置举例 堆大小设置 JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制.32位系统 下,一般限制在1.5G~2G;64为操作系统对内存无限制.我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m. 典型设置: java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -Xmx3550m:设置JVM最大可用内存为3550M. -Xms3550m:设置JVM促使内存为3550m.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存. -Xmn2g:设置年轻代大小为2G.整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8. -Xss128k:设置每个线程的堆栈大小.JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右. java -Xmx3550m -Xms3550m
  • 网络通讯面试题及答案
    文章目录 1. BIO 与 NIO 的区别2. select 与 poll 的区别3.请概述 OSI 网络模型4. TCP 和 UDP 的区别5.请概述 TCP 的三次握手四次挥手机制6.为什么 TCP 握手需要三次?7.为什么 TCP 的挥手需要四次?8.什么是 DDOS 攻击9.什么是 SYN 洪水攻击10. HTTP1.0 和 HTTP1.1 的区别11. Https 原理是什么?12. 说说你知道的几种 HTTP 响应码。13. 如何理解 HTTP 协议的无状态性。14. Session 和 cookie 的区别。15. 用户在浏览器输入一个 URL 并回车,这个过程涉 及到哪些网络协议。16. HTTP2.0 带来的变化。17. Rest和Http什么关系?你对Rest风格如何理解?18. 哪些应用比较适合用 udp 实现19. Netty 的特点?20. Netty 的线程模型21. TCP 粘包/拆包的原因及解决方法?22. 请概要介绍下序列化23. Netty 的零拷贝实现24. Netty 的优势有哪些?25. (高)Netty 高性能表现在哪些方面?26. Netty 中有哪些重要组件?27. 如何让单机下 Netty 支持百万长连接? 前 言 本文仅收录了一些常见的网络通讯面试题,如需查看其它java面试题可查看我另一篇博文: JAVA |
  • 记一次接口压力测试与性能调优
    〇、经验总结 1.如果总的CPU占用率偏高,且基本都被业务线程占用时,CPU占用率过高的原因跟JVM参数大小没有直接关系,而跟具体的业务逻辑有关。2.当设置JVM堆内存偏小时,GC频繁会导致业务线程停顿增多,TPS下降,最后CPU占用率也低了;3.当设置JVM堆内存偏大时,GC次数下降,TPS上升,CPU占用率立刻上升。4.Dom4J 这个xml解析工具性能很强大,但在处理节点和层级都较多的xml文本时,整体解析效率依然会成为业务处理瓶颈。 一、背景说明 最近新项目上线,需要对项目中的一个HTTP接口进行压力测试,以保证接口性能稳定性。该接口涉及到的主要业务是接收HTTP请求,获取请求中的xml报文参数,并将xml报文解析后存入MySQL数据库。接口业务流程如下: 该业务接口部署的服务器配置和部署MySQL组件的服务器配置一致,都是4核8G,50G普通硬盘,并且处于同一个内网网段,我们预估的性能指标要达到200并发,500TPS。在压力测试过程中,我们重点关注TPS、GC次数、CPU占用率和接口响应时间等指标。 二、测试过程 完成项目部署后,我们开始编辑jemeter测试脚本,设置压力测试的标准为200个并发线程,在10秒内全部启动,持续压测时间15分钟,接着开始启动jemeter脚本进行测试。 1、第一次压力测试 (1)JVM配置 垃圾收集策略包括:老年代启用CMS垃圾收集算法
  • Spark Streaming 调优实践
    在使用 Spark 和 Spark Streaming 时,当我们将应用部署在集群上时,可能会碰到运行慢、占用过多资源、不稳定等问题,这时需要做一些优化才能达到最好的性能。有时候一个简单的优化可以起到化腐朽为神奇的作用,使得程序能够更加有效率,也更加节省资源。本文我们就来介绍一些能够提高应用性能的参数和配置。另外需要指出的是,优化本身是一个具体性很强的事情,不同的应用及落地场景会有不同的优化方式,并没有一个统一的优化标准。本文我们简单聊聊一些在项目中踩过的“坑”,列举以下常见的优化方式。▌数据序列化在分布式应用中,序列化(serialization)对性能的影响是显著的。如果使用一种对象序列化慢、占用字节多的序列化格式,就会严重降低计算效率。通常在 Spark 中,主要有如下3个方面涉及序列化:① 在算子函数中使用到外部变量时,该变量会被序列化后进行网络传输。② 将自定义的类型作为 RDD 的泛型类型时,所有自定义类型对象都会进行序列化。因此这种情况下,也要求自定义的类必须实现 Serializable 接口。③ 使用可序列化的持久化策略时(比如 MEMORY_ONLY_SER),Spark 会将 RDD 中的每个 partition 都序列化成一个大的字节数组。而 Spark 综合考量易用性和性能,提供了下面两种序列化库。① Java 序列化:默认情况下,Spark 使用
  • Java 应用性能调优工具-----JVisualVM
    JVisualVM 简介 VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。在JDK_HOME/bin(默认是C:\Program Files\Java\jdk1.6.0_13\bin)目录下面,有一个jvisualvm.exe文件,双击打开,从UI上来看,这个软件是基于NetBeans开发的了。 VisualVM 提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于 Java 技术的应用程序的详细信息。VisualVM 对 Java Development Kit (JDK) 工具所检索的 JVM 软件相关数据进行组织,并通过一种使您可以快速查看有关多个 Java 应用程序的数据的方式提供该信息。您可以查看本地应用程序或远程主机上运行的应用程序的相关数据。此外,还可以捕获有关 JVM 软件实例的数据,并将该数据保存到本地系统,以供后期查看或与其他用户共享。 双击启动 jvisualvm.exe,启动起来后和jconsole 一样同样可以选择本地和远程,如果需要监控远程同样需要配置相关参数。 主界面如下;
  • JAVA面试题最新大全(十五)
    JVM 176.说一下 jvm 的主要组成部分?及其作用? 类加载器(ClassLoader)运行时数据区(Runtime Data Area)执行引擎(Execution Engine)本地库接口(Native Interface) 组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。 177.说一下 jvm 运行时数据区? 程序计数器虚拟机栈本地方法栈堆方法区 有的区域随着虚拟机进程的启动而存在,有的区域则依赖用户进程的启动和结束而创建和销毁。 178.说一下堆栈的区别? 栈内存存储的是局部变量而堆内存存储的是实体; 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短; 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。 179.队列和栈是什么?有什么区别? 队列和栈都是被用来预存储数据的。 队列允许先进先出检索元素
  • HBase调优|HBase + G1GC 性能调优
    目前小米已经在线上开始大规模使用G1垃圾回收算法,在论坛中也看到一些朋友在讨论使用G1碰到的各种各样的问题,这里打算写一篇文章记录下调G1的一些经验。先传送门一下,之前在HBaseConAsia2017分享过一个G1GC调优的PPT: http://openinx.github.io/2012/01/01/my-share/首先,对G1算法不熟悉的同学,可以仔细读一读Oracle的G1算法教程,教程基本交代了G1的运行原理以及和CMS本质区别,如果对算法细节感兴趣,可以读一下Garbage-First Garbage Collection这篇论文,JVM的G1实现应该是按照这篇论文来的.为了便于统计G1GC的日志信息,我们需要开启以下所有的G1参数:-Xmx30g -Xms30g-XX:MaxDirectMemorySize=30g-XX:+UseG1GC-XX:+UnlockExperimentalVMOptions-XX:MaxGCPauseMillis=90-XX:G1NewSizePercent=8-XX:InitiatingHeapOccupancyPercent=30-XX:+ParallelRefProcEnabled-XX:ConcGCThreads=4-XX:ParallelGCThreads=16-XX:MaxTenuringThreshold=1-XX
  • 来自 Facebook 的 Spark 大作业调优经验
    来自 Facebook 的 Spark 大作业调优经验 过往记忆大数据 过往记忆大数据 Facebook Spark 的使用情况 在介绍下面文章之前我们来看看 Facebook 的 Spark 使用情况: 如果想及时了解Spark、Hadoop或者HBase相关的文章,欢迎关注微信公众号:iteblog_hadoop •Spark 是 Facebook 内部最大的 SQL 查询引擎(按 CPU 使用率计算)•在存储计算分离的集群上使用 Spark,也就是存储和计算资源可以单独扩展升级。•考虑到 Facebook 的规模,效率是 Spark 的首要任务,主要包括以下两个效率:•计算效率:优化CPU和内存使用,CPU 的 40% 时间花在读写上。•存储效率:优化磁盘大小和IOPS:存储格式对磁盘占用大小和 IOPS 有很大影响,Facebook 数据仓库底层存储格式使用的是 ORC。 扩展 Spark Driver 动态资源分配在 Facebook,Spark 集群启用了动态资源分配(Dynamic Executor Allocation),以便更好的使用集群资源,而且在 Facebook 内部,Spark 是运行在多租户的集群上,所以这个也是非常合适的。比如典型的配置如下: •spark.dynamicAllocation.enabled = true •spark
  • JVM监控和查看
    一、性能调优1.1 性能调优的目的 减少minor gc的频率、将转移到老年代的对象数量降低到最小以及减少full gc的次数,调优的关键是找到性能的瓶颈#此图来自:http://uule.iteye.com/blog/21146971.2 性能调优的手段#文字内容来自:http://blog.csdn.net/wfzczangpeng/article/details/518164091.使用JDK提供的内存查看工具,如JConsole和Java VisualVM 2.控制堆内存各个部分所占的比例 3.采用合适的垃圾收集器手段1:内存查看工具和GC日志分析-verbose.gc:显示GC的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。 -xx:+printGCdetails:详细了解GC中的变化。 -XX:+PrintGCTimeStamps:了解这些垃圾收集发生的时间,自JVM启动以后以秒计量。 -xx:+PrintHeapAtGC:了解堆的更详细的信息。手段2:针对新生代和旧生代的比例如果新生代太小,会导致频繁GC,而且大对象对直接进入旧生代引发full gc 如果新生代太大,会诱发旧生代full gc,而且新生代的gc耗时会延长 建议新生代占整个堆1/3合适,相关JVM参数如下: -Xms:初始堆大小 -Xmx:最大堆大小 -
  • JVM学习笔记(6)运行时数据区之堆
    运行时数据区详解<2>堆 一.堆核心概述内存细分 二.设置堆的大小与OOM堆空间大小设置OutOfMemorry(堆空间溢出)举例 三.年轻代与老年代四.图解对象分配过程概述:总结:特殊情况常用调优工具 五.Minor GC,Major GC , Full GC最简单的分代式GC策略的触发条件年轻代GC(Minor GC) 触发机制老年代GC(Major GC / Full GC) 触发机制Full GC 触发机制(简单描述)示例: 六.堆空间的分代思想七.内存分配策略(对象提升(promotion)规则)示例: 八.为对象分配内存:TLAB对象分配过程: TLAB为什么会有TLAB(Thread Local Allocation Buffer)什么是TLABTLAB的再说明 九.小结堆空间的参数设置-XX:HandlePromotionFailure -> 是否设置空间分配担保 十.堆是分配对象的唯一选择吗逃逸分析概述:参数设置: 逃逸分析:代码优化栈上分配同步省略(消除)标量替换例子 小结 一.堆核心概述 如图所示,开启了两个进程 代码一样,在JDK自带的jvisualvm.exe工具中可以查看到两个进程分别拥有一个堆空间, 内存细分 目前堆空间主要是由新生代和老年代组成的,元空间相当于编外人员, 元空间的具体实现是由方法区来实现的. 是 可使用此jvm参数打印GC参数 -XX
  • 极客大学Java进阶训练营
    极客大学Java进阶训练营 模块一:JVM 进阶 - Java开发者大厂面试必知必会 从 0 掌握 JVM 关键技术,了解核心知识; 全面了解各类 GC 算法的原理和特性,洞悉相关原理; 全面学习常见的 JVM 分析调优工具,上手十八般武艺; 一线大厂 JVM 面试题全面剖析,助力拿到心仪 Offer。 学习和工作中的痛点 没有经过体系化 JVM 学习,不懂原理,做不到知其然知其所以然; 不熟悉 JVM 工具和方法,遇到问题不知道从何下手、如何分析和解决问题; 缺乏实际场景的练习,每次看看书上的知识就忘,理解不深,无法做到融会贯通; V 932891086 欢迎技术交流和分享 在面试过程中十回有九回都遇到问 JVM 理论和分析调优的问题,每次都很难让面试官满意。 通过学习掌握的核心能力 夯实基础:掌握 JVM 的基础知识和常用工具,了解一般原理,知道从什么地方着手分析问题; 深入学习:掌握各类 GC 算法的一般原理,知道如何根据实际需要选择使用合适的 GC 策略; 分析问题:掌握 GC 日志、线程、内存等维度的分析技巧,知道排查问题和优化系统的套路; 积累经验:了解常见的分析调优经验,熟悉常见的面试问题和技巧,彻底学会 JVM 知识。 详细内容 JVM 基础知识、Java 字节码技术、JVM 类加载器、JVM 内存模型、JVM 启动参数详解; JDK 内置命令行工具、JDK
  • 《分享》关于JVM的学习资料推荐
    哈喽哈喽大家猴,我是把代码写成bug的大头菜。原创:大头菜技术(微信公众号ID:bigheadit),欢迎分享,转载请保留出处。 前言 大头菜近日推出了一些关于JVM的相关文章,主要包括JVM的基础知识和JVM的调优实战案例。 《技术周报》 《JVM38问+答案》 以上的文章,都是一种知识的提炼,适合查漏补缺,不适合系统化的学习。关于系统化学习JVM的基础知识。大头菜在这里提供一些学习资料: 《深入理解Java虚拟机》——周志明 《深入拆解Java虚拟机》——极客时间 《Java生产环境下性能监控与调优详解》——慕课网 《Java中9种常见的CMS GC问题分析与解决》——美团 以上这些资料,都是大头菜自己全部看过的,觉得比较好,所以才比较推荐的。 关于基础的知识,这里比较推荐的书籍就是《深入理解Java虚拟机》,不太推荐视频。一是,视频讲的效率有点慢,磨磨蹭蹭的。二是,因为《深入理解Java虚拟机》已经可以把知识点讲得明明白白,里面也有相应的实战案例。 关于极客时间的《深入拆解Java虚拟机》,这个算是一种对《深入理解Java虚拟机》的补充资料,门槛相对来说比较高,适合看完《深入理解Java虚拟机》书籍后再来看。否则会看得比较懵。 关于慕课网的《Java生产环境下的性能监控与调优详解》,里面有很多实战案例,当然也包括JVM的,讲得很清楚,很到位。 最后是《美团技术团队》的技术文章
  • 【JVM】JVM调优(基础篇)
    目录 一、概述 二、知识点划分 1)JVM内存划分 2)回收器算法 3)回收器 一、概述 先来说下JVM调优主要是在调啥? 调优就是调节JVM运行时内存大小+gc垃圾回收细节,要想调整JVM运行时内存大小 需要我们知道JVM内存划分知识以及要想调整gc垃圾回收的细节; 需要我们知道垃圾回收器工作原理以及它们使用的垃圾回收算法; 需要我们知道垃圾回收的一个流程以及调优的基本原则(能在年轻代回收掉的不要留到老年代,减少Full GC 次数); 二、知识点划分 1)JVM内存划分 JVM运行时内存划分图 堆(heap):这块区域主要存放对象的,比如说我们程序中new User()对象它就会被分配到这块区域,这块区域是共享的,也就是所有线程都可以访问该区域的对象,堆也是垃圾回收主要区域。 java虚拟机栈 (vm stack): 这块区域存放我们线程运行时的一些数据,它是每个线程私有的,可以理解为一个线程就对应的着一个java虚拟机栈,它里面就是一个个的栈帧组成的,一个方法就是一个栈帧,栈帧里面有局部变量表,操作数栈,常量池的引用,方法返回地址等; 比如下面这段代码: public static void main(String[] args) { int a=1; int b=2; int c= a+b; System.out.println(c); c=0; } 这段代码就是计算a+b
  • JVM调优典型配置的5种方法!
    常见配置汇总: 1. 堆设置 -Xms:初始堆大小 -Xmx:最大堆大小 -XX:NewSize=n:设置年轻代大小 -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 -XX:MaxPermSize=n:设置持久代大小 2. 收集器设置 -XX:+UseSerialGC:设置串行收集器 -XX:+UseParallelGC:设置并行收集器 -XX:+UseParalledlOldGC:设置并行年老代收集器 -XX:+UseConcMarkSweepGC:设置并发收集器 垃圾回收统计信息 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:filename 3. 并行收集器设置 -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。 -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间 -XX:GCTimeRatio=n
  • JVM之堆
    JVM之堆 核心概述 一个JVM实例值存在一个堆内存,堆也是Java内存管理的核心区域。 Java堆区在JVM启动的时候就被创建了,其空间大小也就确定了,是JVM管理的最大一块内存空间。《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(TLAB,Thread Local Allocation Buffer)。《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上(但并不是全部)。数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。堆,是GC(垃圾收集器)执行垃圾回收的重点区域。 注意: Java 7之前和图上一模一样,Java 8把永久区换成了元空间(方法区) 堆逻辑上由”新生+养老+元空间“三个部分组成,物理上由”新生+养老“两个部分组成 当执行new Person();时,其实是new在新生区的伊甸园区,然后往下走,走到养老区,但是并未到元空间。 虽然说,逻辑上是将堆空间划分为新生代、老年代和永久代三部分,实际上是不考虑永久代(也就是Java8中提到的元空间)的,可以将其看做是方法区的落地实现。 注意: GC发生在伊甸园区
  • android面试题目!Android程序员最大的悲哀是什么?全套教学资料
    背景 小程序从首次发布至今,经过了几十个版本的迭代。随着业务发展,页面功能内容的不断增多,相关性能数据不断变差,核心性能数据 FMP 长期处在 2000ms 以上。 在该项目之前,我们团队也对小程序做了一定的性能调优工作,内容包括: 包体积优化,去除了不少引用在项目中的图片素材文件,将包体积优化至 500kb 以下;联合后端对耗时较高的业务接口做优化,单个接口返回速度需要控制在 100ms 左右;优化了部分业务逻辑,小程序启动时减少了一些不必要的操作逻辑;使用了小程序框架提供的最新生命周期 onInit ,可提前 100ms 左右发起业务网络请求;使用 prelink 预连接网络,提升数据接口的请求效率。 经过上述手段之后,FMP 降到了 1900ms 左右,后续再也无法产生优化效果。 以上优化手段,基本排除了网络连接,包体积优化不到位引起的性能不佳。那么我们就只有一个问题需要仔细排查 —— 内容的渲染效率。 一、Java中高级 1、谈谈对java多态的理解? 2、你所知道的设计模式有哪些? 3、静态代理和动态代理的区别,什么场景使用? 5、简单工厂、工厂方法、抽象工厂、Builder模式的区别? 6、装饰模式和代理模式有哪些区别 ?与桥接模式相比呢? 7、集合框架,list,map,set都有哪些具体的实现类,区别都是什么? 8、HashMap和HashTable的主要区别是什么
  • CMS GC耗时很高优化过程全记录
    1. 背景 多个业务线的应用出现LongGC告警最近一段时间,经常收到CAT报出来的Long GC告警(配置为大于3秒的为Longgc)。 2. 知识回顾 2.1 JVM堆内存划分 新生代(Young Generation) 新生代内被划分为三个区:Eden,from survivor,to survivor。大多数对象在新生代被创建。Minor GC针对的是新生代的垃圾回收。 老年代(Old Generation) 在新生代中经历了几次Minor GC仍然存活的对象,就会被放到老年代。Major GC针对的是老年代的垃圾回收。本文重点分析的CMS就是一种针对老年代的垃圾回收算法。另外Full GC是针对整堆(包括新生代和老年代)做垃圾回收的。 永久代(Perm) 主要存放已被虚拟机加载的类信息,常量,静态变量等数据。该区域对垃圾回收的影响不大,本文不会过多涉及。 2.2 CMS垃圾回收的6个重要阶段 1、initial-mark 初始标记(CMS的第一个STW阶段),标记GC Root直接引用的对象,GC Root直接引用的对象不多,所以很快。 2、concurrent-mark 并发标记阶段,由第一阶段标记过的对象出发,所有可达的对象都在本阶段标记。 3、concurrent-preclean 并发预清理阶段,也是一个并发执行的阶段。在本阶段,会查找前一阶段执行过程中
  • 京东Java大牛分享的性能调优笔记:JVM+MySQL+Tomcat,让你面试无惧
    很多工作两三年的同行都跟我说,认为性能调优没什么用。刚工作的时候我也这样以为,但后来我才知道我当时想法多么的天真。 转折 曾经,我的前前东家系统研发部门来了一位大神,为什么叫他大神?因为在他来公司的一年时间里只做了一件事,就是把服务器的数量缩减了一半,系统的性能指标,反而还提升了。 我有一个在小厂的朋友,有一次跟我说,他们公司的系统从来没有经过性能调优,功能测试完成后就上线了,线上也没有出现过什么性能问题,为什么还要去做性能调优呢? 当时我就回答了他一句,如果你们公司做的是 12306 网站,不做系统性能优化就上线,试试看会是什么情况。一款线上产品如果没有经过性能测试,那它就好比是一颗定时炸弹。 大厂 我们再来看看2019百度、京东、腾讯、阿里巴巴等各大厂关于性能调优的面试题,看看他们对于性能调优看重的地方。 Java虚拟机底层原理与性能优化 Java虚拟机内存模型能说说吗?类加载器的双亲委派模型是什么?JVM垃圾收集算法与收集器有哪些?JVM诊断调优工具用过哪些?每秒几十万并发的秒杀系统为什么会频繁发生GC?日均百万级交易系统如何优化JVM?线上生产系统OOM如何监控及定位与解决?高并发系统如何基于G1垃圾回收器优化性能? 对于上面这些内容一篇文章,难以说完。在这分享一个Java虚拟机底层原理与性能优化学习笔记,由于篇幅限制,在这里就只把实战文档的所含内容全部展现出来了