JVM 以及 Tomcat 调优

版权声明:仅供学习参考 转载请标明出处。 https://blog.csdn.net/xujiamin0022016/article/details/82155283

jvm 调优

参考老男孩的视频做的概念笔记,方便自己使用。还没有在实际操作中使用过 如果有错误欢迎纠正,自己也会在实践中摸索。

什么是JVM

JVM是指java虚拟机

 

 

JVM 内存结构

JVM是按照运行时数据的存储结构来划分内存结构的,JVM在运行Java程序时,将它们划分成几种不同格式的数据,分别存储在不同的区域,这些数据统一称为运行时数据。运行时数据包括Java程序本身的数据信息和JVM运行Java需要的额外数据信息。

 

 

 

方法区   

Java堆

Java栈  核心数据结构  具有后进先出(LIFO),先进后出(FILO)的特性

 

 

JVM内存分配

栈内存分配

保存参数、局部变量、中间计算过程和其他数据。退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁

栈的优点:存取速度比堆快,仅次于寄存器,栈数据可以共享

栈的缺点:存在栈中的数据大小、生存期是在编译时就确定的,导致其缺乏灵活性

堆内存分配

堆的优点:动态地分配内存大小,生存期不必事先告诉编译器,它是在运行期动态分配的,垃圾回收器会自动收走不再使用的空间区域

堆的缺点:运行时动态分配内存,在分配和销毁时都要占用时间,因此堆的效率较低

 

应用要看是io密集型 还是cpu密集型  一般 io

 

 

JVM堆结构

Java堆结构和垃圾回收

 

调优 主要避免full gc

 

 

JVM堆配置参数

1、-Xms初始堆大小

默认物理内存的1/64(<1GB)

2、-Xmx最大堆大小

默认物理内存的14(<1GB),实际中建议不大于4GB

3、一般建议设置-Xms=-Xmx

好处是避免每次在gc后,调整堆的大小,减少系统内存分配开销。

4、整个堆大小=年轻代大小+年老代大小+持久代大小

 

 

JVM新生代(young generation)

1、新生代=1个eden区+2个Survivor区
2、-Xmn年轻代大小(1.4版本之后)
-XX: Newsize,-XX:MaxNewsize(设置年轻代大小(for1.3/1.4)
默认值大小为整个堆的3/8
3、-XX: NewRatio
年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
4、-XX: SurvivorRatio
Eden区与Suovivor区的大小比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
5、用来存放JVM刚分配的Java对象。

 

java老年代(tenured generation)

1、老年代=整个堆-年轻代大小-持久代大小
2、年轻代中经过垃圾回收没有回收掉的对象被复制到年老代。
3、老年代存储对象比年轻代年龄大的多,而且不乏大对象。

4新建的对象也有可能直接进入老年代
4.1大对象,可通过启动参数置。-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配
4.2大的数组对象,切数组中无引用外部对象
5、老年代大小无配置参数

 

 

 

 

通常情况下   e区经过s0 s1的young gc过程之后到达老年代的理想大小只有1m

但是如果到达老年代的大小大于老年代剩余空间,就会出现full gc ,将年轻代和老年代全部清空。缓存数据就没了。再将10m数据放进去

如果缓存带是长引用,就会出现outofmemery

 

 

Java持久代方法区(perm generation)

1 持久代=整个堆年轻代大小老年代大小
2  -XX:PermSize –XX:MaxSize
设置持久代的大小,一般情况推荐把-XX:PermSize设置成-XX:MaxPermSize的值为相同的值,因为永久代大小的调整也会导致堆内存需要触发fgc
3 存放Class、Method元信息,其大小与项目的规模、类、方法的数量有关。一般设置为128M就足够,设置原则是预留30%的空间
4 永久代的回收方式
4.1 常量池中的常量,无用的类信息,常量的回收很简单,没有引用了就可以被回收

4.2 对于无用的类进行回收,必须保证3点
类的所有实例都已经被回收
加载类的ClassLoader已经被回收
类对象的Class对象没有被引用(即没有通过反射引用该类的地方)

 

 

 

程序在运行之后 会在用到类库jar的时候 去lib中调用 导致其增大。会导致方法区 溢出。可以适当增加方法区大小128*2

jvm内存垃圾回收

jvm垃圾收集算法

1、引用计数算法(JDK1.2之前)
每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。还有一个问题是如何解决精准计数。
2、根搜索算法
从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的不可达对象
在Java语言中,GC Roots包括:
虚拟机栈中引用的对象
方法区中类静态属性实体引用的对象
方法区中常量引用的对象
本地方法栈中JVM引用的对象

 

 

jvm垃圾回收算法

1 复制算法

2 标记清楚算法

3 标记整理压缩算法

 

复制算法

复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但带来的成本是需要一块内存交换空间用于进行对象的移动
此算法用于新生代内存回收,从E区回收到S0或者S1

 

 

标记清除算法

标记清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如图所示
标记清除算法不需要进行对象的移动
仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记清除算法直接回收不存活的对象,因此会造成内存碎片!

 

标记整理算法

标记整理算法采用标记清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动并更新对应的指针。标记整理算法是在标记清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题

 

 

 

JVM回收方式

1、串行回收
gc单线程内存回收、会暂停所有用户线程
2、并行回收
收集是指多个GC线程并行工作,但此时用户线程是暂停的;所以Seria是串行的, parallel收集器是并行的,而CMS收集器是并发的
3、并发回收
是指用户线程与GC线程同时执行(不一定是并行,可能交替,但总体上是在同时执行的),不需要停顿用户线程(其实在CMS中用户线程还是需要停顿的,只是非常短,GC线程在另一个CPU上执行)

 

 

 

 

JVM回收器

JVM常见垃圾回收器

下图是HotSpot里的收集器,中间的横线表示分代,有连线表示可以组合使用

Serial回收器(串行回收器)

1、是一个单线程的收集器,只能使用一个CPU或一条线程去完成垃圾收集;在进行垃圾收集时,必须暂停所有其他工作线程,直到收集完成
2、缺点:Stop-The-World
3、优势:简单。对于单CPU的情况,由于没有多线程交互开销,反而可以更高效。是Client模式下默认的新生代收集器

 

新生代Serial回收器

1、-XX:+UseSeriaIGO来开启
Serial New+ Serial Old的收集器组合进行内存回收

2、使用复制算法。
3、独占式的垃圾回收。
一个线程进行GC,串行。其它工作线程暂停。

 

老年代Serial回收器

1、-XX:+UseSeriaIGO来开启
Seral New+Serial Old的收集器组合进行内存回收

2、使用标记-压缩算法
3、串行的、独占式的垃圾回收器。

因为内存比较大原因,回收比新生代慢

 

 

ParNew回收器(并行回收器)

并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。

 

 

新生代ParNew回收器

1、-XX:+UseParNewGC开启
新生代使用并行回收收集器,老年代使用串行收集器
2、-XX:ParalleIGCThreads指定线程数
默认最好与CPU数量相当,避免过多的线程数影响垃圾收集性能

3、使用复制算法。
4、并行的、独占式的垃圾回收器。

 

 

新生代Parallel Scavenge回收器

1、吞吐量优先回收器
关注CPU吞吐量,即运行用户代码的时间/总时间,比如:JVM运行100分钟,其中运行用户代码99分钟,垃圾收集1分钟,则吞吐量是99%,这种收集器能最高效率的利用CPU,适合运行后台运算
2、-XX:+UseParalleIGC开启
使用Parallel Scavenge+Serial Old收集器组合回收垃圾,这也是在Server模式下的默认值
3、-XX:GCTimeRatio
来设置用户执行时间占总时间的比例,默认99,即1%的时间用来进行垃圾回收
4、-XX:MaxGCPauseMillis
设置GC的最大停顿时间
5、使用复制算法

 

 

老生代Parallel Old回收器

1、-XX:+UseParallelOldGC开启。
使用Parallel Scavenge+Parallel Old组合收集器进行收集

2、使用标记整理算法.
3、并行的、独占式的垃圾回收器。

 

CMS(并发标记清除)收集器  重点

运作过程分为4个阶段
初始标记(CMS initial mark):值标记GC Roots能直接关联到的对象
并发标记(CMS concurrent mark):进行GC Roots Tracing的过程
重新标记(CMS remark):修正并发标记期间因用户程序继续运行而导致标记发生改变的那一部分对象的标记
并发清除(CMS concurrent sweep)
其中标记和重新标记两个阶段仍然需要Stop-The-World,整个过程中耗时最长的并发标记和并发清除过程中收集器都可以和用户线程一起工作。

重点

1、标记-清除算法
同时它又是一个使用多线程并发回收的垃圾收集器
2 –XX:ParallelCMSThreads
手工设定CMS的线程数量,CMS默认启动的线程数是(ParallelGCThreads+3)/4)

3、-XX:+UseConcMarkSweepGC开启
使用ParNew+CMS+Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode failure”失败后的后备收集器使用。
4、-XX:CMSInitiatingOccupancyFraction
设置CMS收集器在老年代空间被使用多少后触发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70
5、-XX:+UseCMSCompactAtFullCollection
由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效。
6、-XX:+CMSFullGCBeforeCompaction
设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用
7 -XX:CMSInitiatingPermOccupancyFraction
设置Perm Gen使用到达多少比率时触发,默认92%

第3条UseConcMarkSweepGC的Old 会触发full gc

正在进行中和没有进行中的,收到大于剩余空间的垃圾回收都会触发full gc

 

GC性能指标

吞吐量     应用花在非GC上的时间百分比
GC负荷    与吞吐量相反,指应用花在GC上的时间百分比
暂停时间   应用花在GC stop-the-world的时间
GC频率     顾名思义
反应速度    从一个对象变成垃圾到这个对象被回收的时间
一个交互式的应用要求暂停时间越少越好,然而,一个非交互性的应用(计算),当然是希望GC负荷越低越好。
一个实时系统对暂停时间和GC负荷的要求,都是越低越好。
一个嵌入式系统当然希望Footprint越小越好(可以忽略)。

 

内存容量配置原则

1年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)在此种情况下,年轻代收集发生的频率也是最小的同时,减少到达年老代的对象
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用
避免设置过小当新生代设置过小时会导致1.YGC次数更加频繁2.可能导致YGC对象直接进入旧生代,如果此时旧生代满了,会触发FGC

2年老代大小选择
响应时间优先的应用:年老代使用并发收集器所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间.最优化的方案,一般需要参考以下数据获得:

并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例

吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象而年老代仅存放长期存活对象

 

 

 

总结一下 调优主要针对以下 (除去Java栈 方法栈)的区域  重点Java堆

tomcat 调优

外部调优

1操作系统

2JVM

3代理-nginx

内部调优

1禁用DNS查询

2调整线程数

3压缩

jvm 调优的终极目标

减少full gc 的频率   full gc会造成应用的暂停

 

maxThreads="600"#最大线程数

minSpareThreads="100" #初始化时创建的线程数

maxSpareThreads="500" #一旦创建的线程超过这个值,Tomcat就会关闭不再需要的 socket线程

acceptCount="700" #指定当所有可以使用的处理请求的线程数都被使用时可以放到处理队列中的请求数,超过这个数的请求将不予处理

 

1关闭dns查询   enableLookups false

2 设置jvm堆内存大小catalina.sh 

3使用线程池,用较少的线程处理较多的访问,可以提高tomcat处理请求的能力。

编辑配置文件 server.xml :

vi  /usr/local/tomcat8/conf/server.xml

 

参数讲解:

name: 线程名称

namePrefix: 线程前缀

maxThreads : 最大并发连接数,不配置时默认200,一般建议设置500~ 800 ,要根据自己的硬件设施条件和实际业务需求而定。

minSpareThreads:Tomcat启动初始化的线程数,默认值25  

prestartminSpareThreads:在tomcat初始化的时候就初始化minSpareThreads的值, 不设置true时minSpareThreads  

maxQueueSize: 最大的等待队列数,超过则拒绝请求

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/xujiamin0022016/article/details/82155283