Jvm优化指南

简介: Jvm优化指南

Jvm优化指南

Jvm简介

JVMJava Virtual MachineJava虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

Java Hotspot vm参数配置

-client -server -classpath / -cp

-verbose :class 输出jvm载入类的相关信息,当jvm报告说找不到类或者类冲突时可此进行诊断。

-verbose:gc 输出每次GC的相关情况

-verbose:jni 输出native方法调用的相关情况,一般用于诊断jni调用错误信息。

jvm参数

Heapsize 堆栈大小

-Xms 定义最小值 -Xmx 定义最大值  建议设置相同,防止垃圾收集器在最小、最大之间收缩堆而产生额外开销。

GC垃圾回收--新生代New配置

-XX:newSize

-XX:MaxNewSize

建议设置相同,防止年轻代的堆收缩。

GC垃圾回收旧生代Old配置 -XX:NewRadio=2表示新生代:老年代 = 1:2

Jit编译器:c1/c2

HotSpot VM中内嵌有两个JIT编译器,分别为Client CompilerServer Compiler,但大多数情况下我们简称为C1编译器和C2编译器。开发人员可以通过如下命令显式指定Java虚拟机在运行时到底使用哪一种即时编译器,如下所示:

C1编译器会对字节码进行简单和可靠的优化,以达到更快的编译速度;而C2编译器会启动一些编译耗时更长的优化,以获取更好的编译质量。

垃圾回收

Java的堆内存模型

 

新生代(Young generation)

绝大多数被新建的对象会分配到这里,由于大部分对象在创建后会很快变得不可达,所以很多对象被创建在新生代,然后消失,对象消失的过程称为”minor GC”

新生代存在1eden区和2survivor,新对象会首先分配到eden,当然如果对象过大会直接分配到老年代,gc,eden中的对象会被移动到survivor,直到对象满足一定年纪(熬过gc的次数),会被移动到老年代

可以设置新生代和老年代的相对大小,这种方式的优点是新生代的大小随着堆内存的动态扩展,设置方式为-XX:NewRatio,例如-XX:NewRatio=8代表老年代/新生代为8/1,老年代占堆内存的7/8,新生代占1/8

老年代(Old generation)

对象没有变的不可达,在新生代中存活下来的对象会被拷贝到这里,其占用的空间比新生代多,因为其占用空间大,所以发生在老年代上的gc比新生代少,对象从老年代消失的过程称之为”major GC”或者”full GC”

永久代(permanent generation)

像一些类级的层次信息,方法数据和方法信息,运行时常量(jdk1.7之后移除永久代),已确定的符号引用和虚方法表等等,它几乎是静态的很少被回收,jdk8之前的hotspot虚拟机中,类的这些永久的数据存放在一个叫做永久代的区域,jvm启动时可以设置-XX:MaxPermsize的值来控制永久代的大小,但是jdk1.8之后取消了永久代,这些元素被移到了与堆不相连的本地内存区域

Gc参数

hotspot实现垃圾回收细节

一致性

一致性要求gc进行时必须停顿所有java执行线程

安全点

程序只有达到安全点才能暂停,安全点的标准就是是否让程序长时间执行的特征,比如方法调用和循环跳转这种,具有这些才会产生安全点

程序暂停的2种方式

抢先式中断:gc发生时,主动中断所有线程

主动式中断:设一个标志,各线程去轮询这个标志,遇到中断则暂停,轮询地方与安全点重合

垃圾收集器

Serial收集器

Serial收集器是最基本,发展历史最悠久的收集器,曾经(jdk1.3.1之前)是虚拟机新生代收集的唯一选择

这是一个单线程收集器。意味着它只会使用一个 CPU 或一条收集线程去完成收集工作,并且在进行垃圾回收时必须暂停其它所有的工作线程直到收集结束。

它的优势在于简单而高效,对于限定单个CPU来说,由于serial收集器没有线程交互的开销,专心做垃圾收集自然能获得最高的单线程收集效率

ParNew 收集器

ParNew收集器其实就是serial收集器的多线程版本,除了使用多线程执行垃圾回收外,其余行为跟serial收集器一模一样,例如在回收算法和回收策略,分配规则都是一样的,在实现上这2种收集器共用了很多代码

Parnew的应用场景:是运行在server模式下的虚拟机的首选新生代垃圾回收器,因为它是除serial收集器外唯一能与cms收集器配合工作

Serial收集器与parnew收集器比较

Parnew收集器在单cpu环境下绝对不会比serial收集器有更好的效果,因为它存在线程的交互开销,但是随着可以使用的CPU数量增加,它对于gc时系统资源的有效利用还是很好的

Parallel Scavenge 收集器

是一个新生代收集器,也是使用复制算法实现,同时也是并行的多线程收集器。应用场景主要是针对那些需要频繁与用户交互的程序,良好的响应速度能提升用户体验

Serial Old 收集器

Serial收集器的老年代版本,单线程,使用标记整理算法。

应用场景:

Client模式:serial old收集器主要意义在于给client模式下的虚拟机使用

Server模式:server模式下有2大用途:一种用途是在jdk1.5之前与Parallel Scavenge 收集器配合使用,另一种用途是作为cms收集器的备用预案,在并发收集失败时使用

Parallel Old 收集器

Parallel Old Parallel Scavenge 收集器的老年代版本。多线程,使用标记整理算法

应用场景:在注重吞吐量以及CPU资源敏感的场合,可以优先考虑Parallel Old Parallel Scavenge 收集器

这个收集器是在jdk1.6中才提供的

CMS 收集器

CMS (Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记清除算法实现。

运作步骤:

初始标记(CMS initial mark):标记 GC Roots 能直接关联到的对象,需要”stop the world”

并发标记(CMS concurrent mark):进行 GC Roots Tracing

重新标记(CMS remark):修正并发标记期间的变动部分,仍然需要”stop the world”

并发清除(CMS concurrent sweep):会清除对象

缺点:

1,cms收集器对CPU资源比较敏感,cms默认启动的回收线程数是(CPU数量+3)/4,也就是CPU4个以上时,并发回收时垃圾收集线程不少于25%CPU资源,并随着CPU数量的增加而下降,但是,如果CPU不足4个时,cms对用户程序的影响变得很大

2,cms收集器无法处理浮动垃圾,可能出现”concurrent mode failure”失败而导致另一次full gc产生

3,cms收集器会产生大量空间碎片,因为它是基于标记-清除算法的收集器,故在收集结束时会产生大量空间碎片,空间碎片过多会给大对象分配带来很大麻烦,比如老年代还有很大空间剩余,但是无法找到更大的连续空间来分配对象从而不得不提前触发一次full gc

G1 收集器

G1(garbage first)是一款面向服务端应用的垃圾收集器,Hotspot开发团队是希望用它替换掉jdk1.5中发布的cms收集器

G1的优点:并行与并发、分代收集、空间整合、可预测停顿。

运作步骤:

 

初始标记(Initial Marking)

并发标记(Concurrent Marking)

最终标记(Final Marking)

筛选回收(Live Data Counting and Evacuation)

实例分析

实例1

java.lang.OutOfMemoryError: GC overhead limit exceeded

通过Linux查看进程的命令ps -ef|grep java发现

-xms768m -xmx768m -xx:NewSize=320m, -xx:MaxNewSize=320m

通过分析应用堆区内存设置只有768m,机器内存有2g,机器上只跑了一个java应用,没有其他需要占内存的地方,另外,这个应用比较大,需要占得内存比较比较多所以修改配置如下

-xms1280m -xmx1280m -xx:NewSize=500m, -xx:MaxNewSize=500m

跟踪运行情况发现,异常没有再出现,问题解决

实例2

一个服务系统,经常出现卡顿,分析原因,发现full gc时间太长

Jstat -gcutil:

S0     S1    E     O     P       YGC     YGCT   FGC   FGCT     GCT

12.16  0.00  5.18   63.78  20.32    54    2.047    5     6.946    8.993

分析上面的数据,发现YGC执行54,耗时2.047,每次YGC耗时37ms,在正常范围,FGC执行了5,耗时6.946,每次耗时1.389秒数据显示问题

Full gc耗时较长,分析该系统发现-XX:NewRadio=9,也就是新生代和老年代的比例是1:9

这会造成:

1,新生代内存太小,导致对象提前进入老年代,从而触发full gc

2,老年代较大,进行full gc耗时较长

解决方法是将-XX:NewRadio=4,意思是将对象控制在新生代就清理掉,没有进入老年代(这种做法针对一些应用有效,并不是所有应用都有效)

总结

1,多数的Java应用不需要在服务器上进行GC优化;

2,多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;

3,在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合)

4,减少创建对象的数量

5,减少使用全局变量和大对象;

6,GC优化是到最后不得已才采用的手段

7,在实际使用中,分析GC情况优化代码比优化GC参数要多得多

GC优化的目的有2

1,将转移到老年代的对象数量降低到最少

2,减少full gc的执行时间

为达目的可以这样做:

1,减少使用全局变量和大对象

2,调整新生代的大小到最合适

3,设置老年代的大小为最合适

4,选择合适的GC收集器

 

 

 

 

 

 

 

相关文章
|
6月前
|
前端开发 Java 编译器
深入理解jvm - 编译优化(上)
深入理解jvm - 编译优化(上)
115 0
|
6月前
|
缓存 监控 算法
jvm性能调优实战 - 39一次大促导致的内存泄漏和Full GC优化
jvm性能调优实战 - 39一次大促导致的内存泄漏和Full GC优化
174 0
|
6月前
|
架构师 Java
jvm性能调优实战 - 35电商APP后台系统如何对Full GC进行深度优化
jvm性能调优实战 - 35电商APP后台系统如何对Full GC进行深度优化
98 0
|
6月前
|
监控 数据可视化 Java
jvm性能调优实战 - 31从测试到上线_如何分析JVM运行状况及合理优化
jvm性能调优实战 - 31从测试到上线_如何分析JVM运行状况及合理优化
94 1
|
4月前
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
46 0
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
存储 算法 Java
深入理解Java虚拟机(JVM)及其优化策略
【10月更文挑战第10天】深入理解Java虚拟机(JVM)及其优化策略
41 1
|
1月前
|
监控 Java
Java的JVM如何优化?
Java的JVM如何优化?
56 3
|
4月前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
94 3
|
4月前
|
监控 Java 中间件
FGC频繁导致CPU 飙升定位及JVM配置优化总结
FGC频繁导致CPU 飙升定位及JVM配置优化总结
160 0