pull/1/head
595208882@qq.com 3 years ago
parent 0288979bc4
commit a461abf6cc

@ -2350,3 +2350,190 @@ mysql> show variables like 'slave_parallel%';
- 提高从库机器的配置减少主库写binlog和从库读binlog的效率差
- 尽量采用短的链路也就是主库和从库服务器的距离尽量要短提升端口带宽减少binlog传输的网络延时
- 实时性要求的业务读强制走主库,从库只做灾备,备份
# 常见问题
## 问题一MySQL事务
**问题一什么Mysql的事务事务的四大特性事务带来的什么问题**
在Mysql中事务的四大特性简称为`ACID`)主要包含:
- **原子性Atomicity**:是指事务的原子性操作,**对数据的修改要么全部执行成功,要么全部失败,实现事务的原子性**是基于日志的Redo/Undo机制
- **一致性**:是指**执行事务前后的状态要一致**,可以理解为数据一致性
- **隔离性**:侧重指**事务之间相互隔离,不受影响**,这个与事务设置的隔离级别有密切的关系
- **持久性**:则是指在**一个事务提交后,这个事务的状态会被持久化到数据库中,也就是事务提交,对数据的新增、更新将会持久化到数据库中**
在我的理解中:**原子性、隔离性、持久性都是为了保障一致性而存在的,一致性也是最终的目的**。
Mysql中事务的隔离级别分为四大等级
- **读未提交READ UNCOMMITTED**
- **读提交 READ COMMITTED**
- **可重复读 REPEATABLE READ**
- **串行化 SERIALIZABLE**
没有哪种隔离级别是完美的只能根据自己的项目业务场景去评估选择最适合的隔离级别大部分的公司一般选择Mysql默认的隔离级别**可重复读**。隔离级别从:**读未提交-读提交-可重复读-串行化**,级别越来越高,隔离也就越来越严实,到最后的串行化,当出现读写锁冲突的时候,后面的事务只能等前面的事务完成后才能继续访问。
- **读未提交:读取到别的事务还没有提交的数据,从而产生了脏读**
- **读提交:读取别的事务已经提交的数据,从而产生不可重复读**
- **可重复读事务开启过程中看到的数据和事务刚开始看到的数据是一样的从而产生幻读在Mysql的中通过MVCC多版本控制的一致性视图解决了不可重复读问题以及通过间隙锁解决了幻读问题**
- **串行化:对于同一行记录,若是读写锁发生冲突,后面访问的事务只能等前面的事务执行完才能继续访问**
## MySQL存储引擎
**问题二MySQL存储引擎的InnoDB和MyISAM有什么区别**
- InnoDB和MyISAM都是Mysql的存储引擎现在MyISAM也逐渐被InnoDB给替代主要因为InnoDB支持事务和行级锁MyISAM不支持事务和行级锁MyISAM最小锁单位是表级。因为MyISAM不支持行级锁所以在并发处理能力上InnoDB会比MyISAM好
- 数据的存储上MyISAM的索引也是由B+树构成,但是树的叶子结点存的是行数据的地址,查找时需要找到叶子结点的地址,再根据叶子结点地址查找数据
- 数据文件构成MyISAM有三种存储文件分别是扩展名为`.frm`(文件存储表定义)、`.MYD` (MYData数据文件)、`.MYI` (MYIndex索引文件)。而InnoDB的表只受限于操作系统文件的大小一般是2GB
- 查询区别对于读多写少的业务场景MyISAM会更加适合而对于update和insert比较多的场景InnoDB会比较适合。
- coun(\*)区别select count(\*) from tableMyISAM引擎会查询已经保存好的行数这是不加where的条件下而InnoDB需要全表扫描一遍InnoDB并没有保存表的具体行数
- 其它的区别InnoDB支持外键但是不支持全文索引而MyISAM不支持外键支持全文索引InnoDB的主键的范围比MyISAM的大
## SQL查询流程
**问题三SQL查询流程是什么**
- **1 连接**
  1.1客户端发起一条Query请求监听客户端的连接管理模块接收请求
  1.2将请求转发到‘连接进/线程模块’
  1.3调用‘用户模块’来进行授权检查
  1.4通过检查后,‘连接进/线程模块’从‘线程连接池’中取出空闲的被缓存的连接线程和客户端请求对接,如果失败则创建一个新的连接请求
- **2 处理**
  2.1先查询缓存检查Query语句是否完全匹配接着再检查是否具有权限都成功则直接取数据返回
  2.2上一步有失败则转交给‘命令解析器’,经过词法分析,语法分析后生成解析树
  2.3接下来是预处理阶段,处理解析器无法解决的语义,检查权限等,生成新的解析树
  2.4再转交给对应的模块处理
  2.5如果是SELECT查询还会经由查询优化器做大量的优化生成执行计划
  2.6模块收到请求后,通过‘访问控制模块’检查所连接的用户是否有访问目标表和目标字段的权限
  2.7有则调用表管理模块先是查看table cache中是否存在有则直接对应的表和获取锁否则重新打开表文件
  2.8根据表的meta数据获取表的存储引擎类型等信息通过接口调用对应的存储引擎处理
  2.9上述过程中产生数据变化的时候,若打开日志功能,则会记录到相应二进制日志文件中
- **3 结果**
  3.1Query请求完成后,将结果集返回给‘连接进/线程模块’
  3.2返回的也可以是相应的状态标识,如成功或失败等
  3.3‘连接进/线程模块’进行后续的清理工作,并继续等待请求或断开与客户端的连接
SQL查询流程图如下
**![SQL查询流程图](images/Database/SQL查询流程图.png)**
## MySQL Log
**问题四redo log和binlog了解过吗**
- redo log
- `redo log`日志也叫做`WAL`技术(`Write- Ahead Logging`),他是一种**先写日志,并更新内存,最后再更新磁盘的技术**为了就是减少sql执行期间的数据库io操作并且更新磁盘往往是在Mysql比较闲的时候这样就大大减轻了Mysql的压力
- `redo log`是固定大小是物理日志属于InnoDB引擎的并且写redo log是环状写日志的形式
- `redo log`日志实现了即使在数据库出现异常宕机的时候,重启后之前的记录也不会丢失,这就是`crash-safe`能力
- binlog
- `binlog`称为**归档日志**是逻辑上的日志它属于Mysql的Server层面的日志记录着sql的原始逻辑主要有两种模式**一个是statement格式记录的是原始的sql而row格式则是记录行内容**。
redo log和binlog记录的形式、内容不同这两者日志都能通过自己记录的内容恢复数据。之所以这两个日志同时存在是因为刚开始Mysql自带的引擎MyISAM就没有crash-safe功能的并且在此之前Mysql还没有InnoDB引擎Mysql自带的binlog日志只是用来归档日志的所以InnoDB引擎也就通过自己redo log日志来实现crash-safe功能。
## MySQL索引种类
**问题五:你知道有哪些种类的索引?**
索引从**数据结构**进行划分的分为:**B+树索引、hash索引、R-Tree索引、FULLTEXT索引**。
索引从**物理存储**的角度划分为:**聚族索引**和**非聚族索引**。
从**逻辑的角度**分为:**主键索引**、**普通索引、唯一索引、联合索引**以及**空间索引**。
## MySQL索引
**问题六:怎么查看索引是否生效?什么情况下索引会失效呢?**
查看索引是否起作用可以使用explain关键字查询后的语句中的key字段若是使用了索引该字段会展示索引的名字。
![MySQL索引-explain](images/Database/MySQL索引-explain.jpg)
- `id`:查询的序列号
- `select_type`:查询类型
- `table`:查询表名
- `type`扫描方式all表示全表扫描
- `possible_keys`:可是使用到的索引
- `key`:实际使用到的索引
- `rows`该sql扫面了多少行
- `Extra`sql语句额外的信息比如排序方式
- **where条件查询中使用了or关键字**有可能使用了索引进行查询也会导致索引失效若是想使用or关键字又不想索引失效只能在or的所有列上都建立索引。
![MySQL索引-or使索引失效](images/Database/MySQL索引-or使索引失效.jpg)
- **条件查询中使用like关键字并且不符合最左前缀原则**,会导致索引失效。
![MySQL索引-like使索引失效](images/Database/MySQL索引-like使索引失效.png)
- **条件查询的字段是字符串而错误的使用where column = 123 数字类型**也会导致索引失效。
![MySQL索引-数据类型不匹配使索引失效](images/Database/MySQL索引-数据类型不匹配使索引失效.png)
- 对于**联合索引查询不符合最左前缀原则**,也会导致索引失效,如下所示:
```sql
alter table user add index union_index(name, age) // name左边的列 age 右边的列
select * from user where name = 'lidu' // 会用到索引
select * from user where age = 18 // 不会使用索引
```
- 在**where条件查询的后面对字段进行null值判断**会导致索引失效解决方式为可以把null改为0或者-1这些特殊的值代替
```sql
SELECT id FROM table WHERE num is null
```
- 在**where子句中使用!= ,< >这样的符号**,也会导致索引失效。
```sql
SELECT id FROM table WHERE num != 0
```
- **where条件子句中=的左边使用表达式操作或者函数操作**,也会导致索引失效。
```sql
SELECT id FROM user WHERE age / 2 = 1
SELECT id FROM user WHERE SUBSTRING(name,1,2) = 'lidu'
```

469
JVM.md

@ -1373,6 +1373,475 @@ CMS垃圾收集器JVM参数最佳实践
- JNI 调用需要谨慎,不一定可以提升性能,反而可能造成 GC 问题
- 升级 JDK 版本到 14避免 [JDK-8048556](https://bugs.openjdk.java.net/browse/JDK-8048556) 导致的重复 GC
# JVM性能调优
## 磁盘不足排查
其实磁盘不足排查算是系统、程序层面的问题排查并不算是JVM但是另一方面考虑过来就是系统磁盘的不足也会导致JVM的运行异常所以也把磁盘不足算进来了。并且排查磁盘不足是比较简单就是几个命令然后就是逐层的排查首先第一个命令就是**df -h**,查询磁盘的状态:
![JVM-磁盘不足排查](images/JVM/JVM-磁盘不足排查.jpg)
从上面的显示中其中第一行使用的2.8G最大,然后是挂载在 **/** 目录下,我们直接**cd /**。然后通过执行:
```shell
du -sh *
```
查看各个文件的大小,找到其中最大的,或者说存储量级差不多的并且都非常大的文件,把那些没用的大文件删除就好。
![JVM-磁盘不足排查-du-sh](images/JVM/JVM-磁盘不足排查-du-sh.jpg)
然后就是直接cd到对应的目录也是执行du -sh *,就这样一层一层的执行,找到对应的没用的,然后文件又比较大的,可以直接删除。
## CPU过高排查
然后就是排查CPU的飙高的原因**CPU飙高的排查都是直接找到对应CPU占比最高的进程然后找到CPU最高的线程**。
总结一下可能导致CPU标高的原因可能是**一个GC线程频繁或者锁资源竞争频繁线程数过多**等原因。
- **GC线程频繁**
- **锁竞争频繁(自旋)**
其中GC线程频繁有可能是**大对象(对象过多),内存泄漏**等原因导致内存紧张一直在执行GC但是每次执行的GC回收的垃圾都非常少。
一般CPU紧张都是线上实施排查并且一般大厂都会有自己自研的监控平台我们自己的监控平台对于我们每台服务器的健康状况健康分、服务期内的应用Mysql、Redis、Mq、Kafka、服务都会进行实施的监控报警所以一般都能都在出现问题前将问题解决掉。
在线上之间也提到过可以使用**top**、**jstack**命令排查CPU飙高的问题。这里有一段案例代码如下
```java
public class CPUSoaring {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
for (;;){
System.out.println("I am children-thread1");
}
}
},"children-thread1");
Thread thread2 = new Thread(new Runnable(){
@Override
public void run() {
for (;;){
System.out.println("I am children-thread2");
}
}
},"children-thread2");
thread1.start();
thread2.start();
System.err.println("I am is main thread!!!!!!!!");
}
}
```
1首先通过**top**命令可以查看到id为**3806**的进程所占的CPU最高
![CPU过高排查-top](images/JVM/CPU过高排查-top.jpg)
2然后通过**top -Hp pid**命令找到占用CPU最高的线程
![CPU过高排查-top-Hp-pid](images/JVM/CPU过高排查-top-Hp-pid.jpg)
3接着通过**printf '%x\n' tid**命令将线程的tid转换为十六进制xid
![CPU过高排查-printf](images/JVM/CPU过高排查-printf.jpg)
4最后通过**jstack pid|grep xid -A 30**命令就是输出线程的堆栈信息,线程所在的位置:
![CPU过高排查-jstack](images/JVM/CPU过高排查-jstack.jpg)
5还可以通过**jstack -l pid > 文件名称.txt** 命令将线程堆栈信息输出到文件,线下查看。
这就是一个CPU飙高的排查过程目的就是要**找到占用CPU最高的线程所在的位置**,然后就是**review**你的代码定位到问题的所在。使用Arthas的工具排查也是一样的首先要使用top命令找到占用CPU最高的Java进程然后使用Arthas进入该进程内**使用dashboard命令排查占用CPU最高的线程。**,最后通过**thread**命令线程的信息。
## OOM异常排查
OOM的异常排查也比较简单首先服务上线的时候要先设置这两个参数
```shell
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目录}
```
指定项目出现OOM异常的时候自动导出堆转储文件然后通过内存分析工具**Visual VM**)来进行线下的分析。
首先我们来聊一聊哪些原因会导致OOM异常站在JVM的分区的角度
- **Java堆**
- **方法区**
- **虚拟机栈**
- **本地方法栈**
- **程序计数器**
- **直接内存**
只有**程序计数器**区域不会出现OOM在Java 8及以上的**元空间**本地内存都会出现OOM。
而站在程序代码的角度来看总结了大概有以下几点原因会导致OOM异常
- **内存泄露**
- **对象过大、过多**
- **方法过长**
- **过度使用代理框架,生成大量的类信息**
接下来我们屋来看看OOM的排查出现OOM异常后dump出了堆转储文件然后打开jdk自带的Visual VM工具导入堆转储文件首先我使用的OOM异常代码如下
```java
import java.util.ArrayList;
import java.util.List;
class OOM {
static class User{
private String name;
private int age;
public User(String name, int age){
this.name = name;
this.age = age;
}
}
public static void main(String[] args) throws InterruptedException {
List<User> list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
Thread.sleep(1000);
User user = new User("zhangsan"+i,i);
list.add(user);
}
}
}
```
代码很简单就是往集合里面不断地add对象带入堆转储文件后在类和实例那栏就可以看到实例最多的类
![OOM异常排查-查看实例最多的类](images/JVM/OOM异常排查-查看实例最多的类.jpg)
这样就找到导致OOM异常的类还可以通过下面的方法查看导致OOM异常的线程堆栈信息找到对应异常的代码段。
![OOM异常排查-查看异常代码](images/JVM/OOM异常排查-查看异常代码.jpg)
![OOM异常排查-查看异常代码堆栈](images/JVM/OOM异常排查-查看异常代码堆栈.jpg)
上面的方法是排查已经出现了OOM异常的方法肯定是防线的最后一步那么在此之前怎么防止出现OOM异常呢
一般大厂都会有自己的监控平台,能够实施的**监控测试环境、预览环境、线上实施的服务健康状况CPU、内存** 等信息对于频繁GC并且GC后内存的回收率很差的就要引起我们的注意了。
因为一般方法的长度合理95%以上的对象都是朝生夕死,在**Minor GC**后只剩少量的存活对象,所以在代码层面上应该避免**方法过长、大对象**的现象。
每次自己写完代码,自己检查后,都可以提交给比自己高级别的工程师**review**自己的代码,就能及时的发现代码的问题,基本上代码没问题,百分之九十以上的问题都能避免,这也是大厂注重代码质量,并且时刻**review**代码的习惯。
## 栈溢出
栈溢出异常的排查(包括**虚拟机栈、本地方法栈**基本和OOM的一场排查是一样的导出异常的堆栈信息然后使用mat或者Visual VM工具进行线下分析找到出现异常的代码或者方法。
当线程请求的栈深度大于虚拟机栈所允许的大小时,就会出现**StackOverflowError**异常,二从代码的角度来看,导致线程请求的深度过大的原因可能有:**方法栈中对象过大,或者过多,方法过长从而导致局部变量表过大,超过了-Xss参数的设置**。
## 死锁排查
死锁的案例演示的代码如下:
```java
public class DeadLock {
public static Object lock1 = new Object();
public static Object lock2 = new Object();
public static void main(String[] args){
Thread a = new Thread(new Lock1(),"DeadLock1");
Thread b = new Thread(new Lock2(),"DeadLock2");
a.start();
b.start();
}
}
class Lock1 implements Runnable{
@Override
public void run(){
try{
while(true){
synchronized(DeadLock.lock1){
System.out.println("Waiting for lock2");
Thread.sleep(3000);
synchronized(DeadLock.lock2){
System.out.println("Lock1 acquired lock1 and lock2 ");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class Lock2 implements Runnable{
@Override
public void run(){
try{
while(true){
synchronized(DeadLock.lock2){
System.out.println("Waiting for lock1");
Thread.sleep(3000);
synchronized(DeadLock.lock1){
System.out.println("Lock2 acquired lock1 and lock2");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
```
上面的代码非常的简单,就是两个类的实例作为锁资源,然后分别开启两个线程,不同顺序的对锁资源资源进行加锁,并且获取一个锁资源后,等待三秒,是为了让另一个线程有足够的时间获取另一个锁对象。
运行上面的代码后,就会陷入死锁的僵局:
![死锁排查-死锁运行结果示例](images/JVM/死锁排查-死锁运行结果示例.jpg)
对于死锁的排查若是在测试环境或者本地直接就可以使用Visual VM连接到该进程如下界面就会自动检测到死锁的存在
![死锁排查-检测死锁](images/JVM/死锁排查-检测死锁.jpg)
并且查看线程的堆栈信息。就能看到具体的死锁的线程:
![死锁排查-查看线程堆栈信息](images/JVM/死锁排查-查看线程堆栈信息.jpg)
线上的话可以上用Arthas也可以使用原始的命令进行排查原始命令可以先使用**jps**查看具体的Java进程的ID然后再通过**jstack ID**查看进程的线程堆栈信息,他也会自动给你提示有死锁的存在:
![死锁排查-jstack查看线程堆栈](images/JVM/死锁排查-jstack查看线程堆栈.jpg)
Arthas工具可以使用**thread**命令排查死锁,要关注的是**BLOCKED**状态的线程,如下图所示:
![死锁排查-Arthas查看死锁](images/JVM/死锁排查-Arthas查看死锁.jpg)
具体thread的详细参数可以参考如下图所示
![死锁排查-Thread详细参数](images/JVM/死锁排查-Thread详细参数.jpg)
**如何避免死锁**
上面我们聊了如何排查死锁,下面我们来聊一聊如何避免死锁的发生,从上面的案例中可以发现,死锁的发生两个线程同时都持有对方不释放的资源进入僵局。所以,在代码层面,要避免死锁的发生,主要可以从下面的四个方面进行入手:
- **首先避免线程的对于资源的加锁顺序要保持一致**
- **并且要避免同一个线程对多个资源进行资源的争抢**
- **另外的话对于已经获取到的锁资源尽量设置失效时间避免异常没有释放锁资源可以使用acquire() 方法加锁时可指定 timeout 参数**
- **最后,就是使用第三方的工具检测死锁,预防线上死锁的发生**
死锁的排查已经说完了上面的基本就是问题的排查也可以算是调优的一部分吧但是对于JVM调优来说重头戏应该是在**Java堆**,这部分的调优才是重中之重。
## 调优实战
上面说完了调优的目的和调优的指标,那么我们就来实战调优,首先准备我的案例代码,如下:
```java
import java.util.ArrayList;
import java.util.List;
class OOM {
static class User{
private String name;
private int age;
public User(String name, int age){
this.name = name;
this.age = age;
}
}
public static void main(String[] args) throws InterruptedException {
List<User> list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
Tread.sleep(1000);
System.err.println(Thread.currentThread().getName());
User user = new User("zhangsan"+i,i);
list.add(user);
}
}
}
```
案例代码很简单,就是不断的往一个集合里里面添加对象,首先初次我们启动的命令为:
```shell
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=50M -Xloggc:./logs/emps-gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./logs/emps-heap.dump OOM
```
就是纯粹的设置了一些GC的打印日志然后通过Visual VM来看GC的显示如下
![调优实战-VisualVM查看GC显示](images/JVM/调优实战-VisualVM查看GC显示.jpg)
可以看到一段时间后出现4次Minor GC使用的时间是29.648ms发生一次Full GC使用的时间是41.944ms。
Minor GC非常频繁Full GC也是在短时间内就发生了几次观察输出的日志发现以及Visual VM的显示来看都是因为内存没有设置太小导致Minor GC频繁。
因此我们第二次适当的增大Java堆的大小调优设置的参数为
```shell
java -Xmx2048m -Xms2048m -Xmn1024m -Xss256k -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=50M -Xloggc:./logs/emps-gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./logs/emps-heap.dump OOM
```
观察一段时间后,结果如下图所示:
![调优实战-一段时间后VisualVM查看GC显示](images/JVM/调优实战-一段时间后VisualVM查看GC显示.jpg)
可以发现Minor GC次数明显下降但是还是发生了Full GC根据打印的日志来看是因为元空间的内存不足看了上面的Visual VM元空间的内存图也是一样基本都到顶了
![调优实战-元空间不足](images/JVM/调优实战-元空间不足.jpg)
因此第三次对于元空间的区域设置大一些并且将GC回收器换成是CMS的设置的参数如下
```shell
java -Xmx2048m -Xms2048m -Xmn1024m -Xss256k -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=50M -Xloggc:./logs/emps-gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./logs/emps-heap.dump OOM
```
观察相同的时间后Visual VM的显示图如下
![调优实战-元空间调后后VisualVM查看GC显示](images/JVM/调优实战-元空间调后后VisualVM查看GC显示.jpg)
同样的时间一次Minor GC和Full GC都没有发生所以这样我觉得也算是已经调优了。
但是调优并不是一味的调大内存是要在各个区域之间取得平衡可以适当的调大内存以及更换GC种类举个例子当把上面的案例代码的Thread.sleep(1000)给去掉。
然后再来看Visual VM的图如下
![调优实战-去掉线程休眠VisualVM显示](images/JVM/调优实战-去掉线程休眠VisualVM显示.jpg)
可以看到Minor GC也是非常频繁的因为这段代码本身就是不断的增大内存直到OOM异常真正的实际并不会这样可能当内存增大到一定两级后就会在一段范围平衡。
当我们将上面的情况再适当的增大内存JVM参数如下
```shell
java -Xmx4048m -Xms4048m -Xmn2024m -XX:SurvivorRatio=7 -Xss256k -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=100m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=50M -Xloggc:./logs/emps-gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./logs/emps-heap.dump OOM
```
可以看到相同时间内确实Minor GC减少了但是时间增大了因为复制算法基本都是存活的复制需要耗费大量的性能和时间
![调优实战-减少MinorGC后VisualVM显示](images/JVM/调优实战-减少MinorGC后VisualVM显示.jpg)
所以调优要有取舍取得一个平衡点性能、状态达到佳就OK了并没最佳的状态这就是调优的基本法则而且调优也是一个细活所谓慢工出细活需要耗费大量的时间慢慢调不断的做对比。
## 调优参数
### 堆
- -Xms1024m 设置堆的初始大小
- -Xmx1024m 设置堆的最大大小
- -XX:NewSize=1024m 设置年轻代的初始大小
- -XX:MaxNewSize=1024m 设置年轻代的最大值
- -XX:SurvivorRatio=8 Eden和S区的比例
- -XX:NewRatio=4 设置老年代和新生代的比例
- -XX:MaxTenuringThreshold=10 设置晋升老年代的年龄条件
### 栈
- -Xss128k
### 元空间
- -XX:MetasapceSize=200m 设置初始元空间大小
- -XX:MaxMatespaceSize=200m 设置最大元空间大小 默认无限制
### 直接内存
- -XX:MaxDirectMemorySize 设置直接内存的容量,默认与堆最大值一样
### 日志
- -Xloggc:/opt/app/ard-user/ard-user-gc-%t.log 设置日志目录和日志名称
- -XX:+UseGCLogFileRotation 开启滚动生成日志
- -XX:NumberOfGCLogFiles=5 滚动GC日志文件数默认0不滚动
- -XX:GCLogFileSize=20M GC文件滚动大小需开 UseGCLogFileRotation
- -XX:+PrintGCDetails 开启记录GC日志详细信息包括GC类型、各个操作使用的时间,并且在程序运行结束打印出JVM的内存占用情况
- -XX:+ PrintGCDateStamps 记录系统的GC时间
- -XX:+PrintGCCause 产生GC的原因(默认开启)
### GC
#### Serial垃圾收集器新生代开启
- -XX:+UseSerialGC 关闭:
- -XX:-UseSerialGC //新生代使用Serial 老年代则使用SerialOld
#### Parallel Scavenge收集器新生代开启
- -XX:+UseParallelOldGC 关闭
- -XX:-UseParallelOldGC 新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器
#### ParallelOl垃圾收集器老年代开启
- -XX:+UseParallelGC 关闭
- -XX:-UseParallelGC 新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器
#### ParNew垃圾收集器新生代开启
- -XX:+UseParNewGC 关闭
- -XX:-UseParNewGC //新生代使用功能ParNew 老年代则使用功能CMS
#### CMS垃圾收集器老年代开启
- -XX:+UseConcMarkSweepGC 关闭
- -XX:-UseConcMarkSweepGC
- -XX:MaxGCPauseMillis GC停顿时间垃圾收集器会尝试用各种手段达到这个时间比如减小年轻代
- -XX:+UseCMSCompactAtFullCollection 用于在CMS收集器不得不进行FullGC时开启内存碎片的合并整理过程由于这个内存整理必须移动存活对象在Shenandoah和ZGC出现前是无法并发的
- -XXCMSFullGCsBefore-Compaction 多少次FullGC之后压缩一次默认值为0表示每次进入FullGC时都进行碎片整理
- -XX:CMSInitiatingOccupancyFraction 当老年代使用达到该比例时会触发FullGC默认是92
- -XX:+UseCMSInitiatingOccupancyOnly 这个参数搭配上面那个用表示是不是要一直使用上面的比例触发FullGC如果设置则只会在第一次FullGC的时候使用-XX:CMSInitiatingOccupancyFraction的值之后会进行自动调整
- -XX:+CMSScavengeBeforeRemark 在FullGC前启动一次MinorGC目的在于减少老年代对年轻代的引用降低CMSGC的标记阶段时的开销一般CMS的GC耗时80%都在标记阶段
- -XX:+CMSParallellnitialMarkEnabled 默认情况下初始标记是单线程的这个参数可以让他多线程执行可以减少STW
- -XX:+CMSParallelRemarkEnabled 使用多线程进行重新标记目的也是为了减少STW
#### G1垃圾收集器开启
- -XX:+UseG1GC 关闭
- -XX:-UseG1GC
- -XXG1HeapRegionSize 设置每个Region的大小取值范围为1MB32MB
- -XXMaxGCPauseMillis 设置垃圾收集器的停顿时间默认值是200毫秒通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的
# JDK Tools
## jps

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Loading…
Cancel
Save