kenchan14

导航

面试题整理

说⼀下 spring 的事务隔离?

https://zhuanlan.zhihu.com/p/147372839
spring 有五⼤隔离级别,默认值为 ISOLATION_DEFAULT(使⽤数据库的设置),其他四个隔离级别和
数据库的隔离级别⼀致:
ISOLATION_DEFAULT:⽤底层数据库的设置隔离级别,数据库设置的是什么我就⽤什么;
ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取
(会出现幻读、脏读、不可重复读);
公众号:Java中文社群
ISOLATION_READ_COMMITTED:提交读,⼀个事务提交后才能被其他事务读取到(会造成幻读、不可
重复读),sql server 的默认级别;
ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同⼀个数据时,其值都和事务开始时候的内
容是⼀致,禁⽌读取到别的事务未提交的数据(会造成幻读),mysql 的默认级别;
ISOLATION_SERIALIZABLE:序列化,代价最⾼最可靠的隔离级别,该隔离级别能防⽌脏读、不可重复
读、幻读。
脏读 :表示⼀个事务能够读取另⼀个事务中还未提交的数据。⽐如,某个事务尝试插⼊记录 A,此时该
事务还未提交,然后另⼀个事务尝试读取到了记录 A。
[脏读:(同时操作都没提交的读取)

脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。

例如:事务T1修改了一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因Rollback了,那么事务T2读取的数据就是脏的。

解决办法:把数据库的事务隔离级别调整到READ_COMMITTED]
不可重复读 :是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
幻读 :指同⼀个事务内多次查询返回的结果集不⼀样。⽐如同⼀个事务 A 第⼀次查询时候有 n 条记
录,但是第⼆次同等条件下查询却有 n+1 条记录,这就好像产⽣了幻觉。发⽣幻读的原因也是另外⼀个
事务新增或者删除或者修改了第⼀个事务结果集⾥⾯的数据,同⼀个记录的数据内容被修改了,所有数
据⾏的记录就变多或者变少了


image
什么是脏读
简单说,读了一条未提交的数据

什么是不可重复读?
一个事务读取了另外一个事务修改后记录 强调的是 update 和delete ,只需要锁住满足条件的记录即可

什么是幻读
一个事务读取了另外一个事务插入的数据,强调的是 insert ,要锁住满足条件及相近的记录。

MVCC 是啥?

MVCC 的英文全称是 Multiversion Concurrency Control ,中文意思是多版本并发控制技术。原理是,通过数据行的多个版本管理来实现数据库的并发控制,简单来说就是保存数据的历史版本。可以通过比较版本号决定数据是否显示出来。读取数据的时候不需要加锁可以保证事务的隔离效果。

MVCC 用来解决什么问题?

一般解决不可重复读和幻读问题,是采用锁机制实现,一种乐观锁的问题去处理,可以采用 MVCC 机制的设计,可以用来解决这个问题。取代行锁,降低系统开销。
读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,读不相互阻塞,写不阻塞读,这样可以提升数据并发处理能力。
降低了死锁的概率,这个是因为 MVCC 采用了乐观锁的方式,读取数据时,不需要加锁,写操作,只需要锁定必要的行。
解决了一致性读的问题,当我们朝向某个数据库在时间点的快照是,只能看到这个时间点之前事务提交更新的结果,不能看到时间点之后事务提交的更新结果。

什么是当前读?

当前读就是读的是最新数据,而不是历史的数据,加锁的 SELECT,或者对数据进行增删改都会进行当前读。

SELECT * FROM player LOCK IN SHARE MODE;
SELECT FROM player FOR UPDATE;
INSERT INTO player values ...
DELETE FROM player WHERE ...
UPDATE player SET ...

InnoDB 的 MVCC 是如何实现的?

InnoDB 是如何存储记录多个版本的?这些数据是 事务版本号,行记录中的隐藏列和Undo Log。

事务版本号

每开启一个日志,都会从数据库中获得一个事务ID(也称为事务版本号),这个事务 ID 是自增的,通过 ID 大小,可以判断事务的时间顺序。

行记录的隐藏列

row_id :隐藏的行 ID ,用来生成默认的聚集索引。如果创建数据表时没指定聚集索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚集索引的方式可以提升数据的查找效率。
trx_id: 操作这个数据事务 ID ,也就是最后一个对数据插入或者更新的事务 ID 。
roll_ptr:回滚指针,指向这个记录的 Undo Log 信息。
image

Undo Log

InnoDB 将行记录快照保存在 Undo Log 里。
image
数据行通过快照记录都通过链表的结构的串联了起来,每个快照都保存了 trx_id 事务ID,如果要找到历史快照,就可以通过遍历回滚指针的方式进行查找。

Read View 是啥?

如果一个事务要查询行记录,需要读取哪个版本的行记录呢? Read View 就是来解决这个问题的。Read View 可以帮助我们解决可见性问题。 Read View 保存了当前事务开启时所有活跃的事务列表。换个角度,可以理解为: Read View 保存了不应该让这个事务看到的其他事务 ID 列表。

trx_ids 系统当前正在活跃的事务ID集合。
low_limit_id ,活跃事务的最大的事务 ID。
up_limit_id 活跃的事务中最小的事务 ID。
creator_trx_id,创建这个 ReadView 的事务ID。
image

如果当前事务的 creator_trx_id 想要读取某个行记录,这个行记录ID 的trx_id ,这样会有以下的情况:

如果 trx_id < 活跃的最小事务ID(up_limit_id),也就是说这个行记录在这些活跃的事务创建前就已经提交了,那么这个行记录对当前事务是可见的。
如果trx_id > 活跃的最大事务ID(low_limit_id),这个说明行记录在这些活跃的事务之后才创建,说明这个行记录对当前事务是不可见的。
如果 up_limit_id < trx_id <low_limit_id,说明该记录需要在 trx_ids 集合中,可能还处于活跃状态,因此我们需要在 trx_ids 集合中遍历 ,如果trx_id 存在于 trx_ids 集合中,证明这个事务 trx_id 还处于活跃状态,不可见,否则 ,trx_id 不存在于 trx_ids 集合中,说明事务trx_id 已经提交了,这行记录是可见的。
ps. 四种情况:1。 小于当前活跃最小id说明已经提交 可见,2.大于当前活跃最大id 说明未提交 当前不可见。3.大于最小活跃id小于最大活跃id 判断是否在集合中 在则说明未提交 不可见,不在则可见

如何查询一条记录

  • 获取事务自己的版本号,即 事务ID
  • 获取 Read View
  • 查询得到的数据,然后 Read View 中的事务版本号进行比较。
  • 如果不符合 ReadView 规则, 那么就需要 UndoLog 中历史快照;
  • 最后返回符合规则的数据
    InnoDB 实现多版本控制 (MVCC)是通过 ReadView+ UndoLog 实现的,UndoLog 保存了历史快照,ReadView 规则帮助判断当前版本的数据是否可见。

总结

如果事务隔离级别是 ReadCommit ,一个事务的每一次 Select 都会去查一次ReadView ,每次查询的Read View 不同,就可能会造成不可重复读或者幻读的情况。
如果事务的隔离级别是可重读,为了避免不可重读读,一个事务只在第一次 Select 的时候会获取一次Read View ,然后后面索引的Select 会复用这个 ReadView.

说⼀下 spring mvc 运⾏流程?

spring mvc 先将请求发送给 DispatcherServlet。
DispatcherServlet 查询⼀个或多个 HandlerMapping,找到处理请求的 Controller。
DispatcherServlet 再把请求提交到对应的 Controller。
Controller 进⾏业务逻辑处理后,会返回⼀个ModelAndView。
Dispathcher 查询⼀个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定的视图对
象。
视图对象负责渲染返回给客户端。

spring cloud 的核⼼组件有哪些?

Eureka:服务注册于发现。
Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
Ribbon:实现负载均衡,从⼀个服务的多台机器中选择⼀台。
Hystrix:提供线程池,不同的服务⾛不同的线程池,实现了不同服务调⽤的隔离,避免了服务雪
崩的问题。
Zuul:⽹关管理,由 Zuul ⽹关转发请求给对应的服务。

spring cloud 断路器的作⽤是什么?

在分布式架构中,断路器模式的作⽤也是类似的,当某个服务单元发⽣故障(类似⽤电器发⽣短路)之
后,通过断路器的故障监控(类似熔断保险丝),向调⽤⽅返回⼀个错误响应,⽽不是⻓时间的等待。
这样就不会使得线程因调⽤故障服务被⻓时间占⽤不释放,避免了故障在分布式系统中的蔓延。

说⼀下 hibernate 的缓存机制?

hibernate 常⽤的缓存有⼀级缓存和⼆级缓存:
⼀级缓存:也叫 Session 缓存,只在 Session 作⽤范围内有效,不需要⽤户⼲涉,由 hibernate ⾃身维
护,可以通过:evict(object)清除 object 的缓存;clear()清除⼀级缓存中的所有缓存;flush()刷出缓
存;
⼆级缓存:应⽤级别的缓存,在所有 Session 中都有效,⽀持配置第三⽅的缓存,如:EhCache。

hibernate 实体类必须要有⽆参构造函数吗?为什么?

hibernate 中每个实体类必须提供⼀个⽆参构造函数,因为 hibernate 框架要使⽤ reflection api,通过
调⽤ ClassnewInstance() 来创建实体类的实例,如果没有⽆参的构造函数就会抛出异常。

mybatis RowBounds 是⼀次性查询全部结果吗?为什么?

RowBounds 表⾯是在“所有”数据中检索数据,其实并⾮是⼀次性查询出所有数据,因为 mybatis 是对
jdbc 的封装,在 jdbc 驱动中有⼀个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,
假如你要查询更多数据,它会在你执⾏ next()的时候,去查询更多的数据。就好⽐你去⾃动取款机取
10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当
你调⽤ next()的时候会⾃动帮你完成查询⼯作。这样做的好处可以有效的防⽌内存溢出。

mybatis 是否⽀持延迟加载?延迟加载的原理是什么?

mybatis ⽀持延迟加载,设置 lazyLoadingEnabled=true 即可。
延迟加载的原理的是调⽤的时候触发加载,⽽不是在初始化的时候就加载信息。⽐如调⽤
a.getB().getName(),这个时候发现 a.getB() 的值为 null,此时会单独触发事先保存好的关联 B 对象的
sql,先查询出来 B,然后再调⽤ a.setB(b),⽽这时候再调⽤ a.getB().getName() 就有值了,这就是延
迟加载的基本原理。(像hashmap1.8中put时才分配数组)

说⼀下 mybatis 的⼀级缓存和⼆级缓存?

⼀级缓存:基于 PerpetualCache 的 HashMap 本地缓存,它的声明周期是和 sqlSession ⼀致
的,有多个 sqlSession 或者分布式的环境中数据库操作,可能会出现脏数据。当 Session flush 或
close 之后,该 Session 中的所有 Cache 就将清空,默认⼀级缓存是开启的。
⼆级缓存:也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作⽤域为 Mapper
级别的,如果多个sqlSession之间需要共享缓存,则需要使⽤到⼆级缓存,并且⼆级缓存可⾃定义
存储源,如 Ehcache。默认不打开⼆级缓存,要开启⼆级缓存,使⽤⼆级缓存属性类需要实现
Serializable 序列化接⼝(可⽤来保存对象的状态)。
开启⼆级缓存数据查询流程:⼆级缓存 -> ⼀级缓存 -> 数据库。
缓存更新机制:当某⼀个作⽤域(⼀级缓存 Session/⼆级缓存 Mapper)进⾏了C/U/D 操作后,默认该作
⽤域下所有 select 中的缓存将被 clear。

kafka 有⼏种数据保留的策略?

kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息⼤⼩保留。

什么情况会导致 kafka 运⾏变慢?

cpu 性能瓶颈
磁盘读写瓶颈
⽹络瓶颈

集群中为什么要有主节点?

在分布式环境中,有些业务逻辑只需要集群中的某⼀台机器进⾏执⾏,其他的机器可以共享这个结果,
这样可以⼤⼤减少重复计算,提⾼性能,所以就需要主节点。

⼀张⾃增表⾥⾯总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,

⼜插⼊了⼀条数据,此时 id 是⼏?
表类型如果是 MyISAM ,那 id 就是 8。
表类型如果是 InnoDB,那 id 就是 6。
InnoDB 表只会把⾃增主键的最⼤ id 记录在内存中,所以重启之后会导致最⼤ id 丢失。

说⼀下乐观锁和悲观锁?

乐观锁:每次去拿数据的时候都认为别⼈不会修改,所以不会上锁,但是在提交更新的时候会判断
⼀下在此期间别⼈有没有去更新这个数据。
悲观锁:每次去拿数据的时候都认为别⼈会修改,所以每次在拿数据的时候都会上锁,这样别⼈想
拿这个数据就会阻⽌,直到这个锁被释放。
数据库的乐观锁需要⾃⼰实现,在表⾥⾯添加⼀个 version 字段,每次修改成功值加 1,这样每次修改
的时候先对⽐⼀下,⾃⼰拥有的 version 和数据库现在的 version 是否⼀致,如果不⼀致就不修改,这
样就实现了乐观锁。

mysql 问题排查都有哪些⼿段?

使⽤ show processlist 命令查看当前所有连接信息。
使⽤ explain 命令查询 sql 语句执⾏计划。
开启慢查询⽇志,查看慢查询的 sql。

如何做 mysql 的性能优化?

为搜索字段创建索引。
避免使⽤ select *,列出需要查询的字段。
垂直分割分表。
选择正确的存储引擎。

redis 持久化有⼏种⽅式?

redis 的持久化有两种⽅式,或者说有两种策略:
RDB(Redis Database):指定的时间间隔能对你的数据进⾏快照存储。
AOF(Append Only File):每⼀个收到的写命令都通过write函数追加到⽂件中。

redis 怎么实现分布式锁?

redis 分布式锁其实就是在系统⾥⾯占⼀个“坑”,其他程序也要占“坑”的时候,占⽤成功了就可以继续执
⾏,失败了就只能放弃或稍后重试。
占坑⼀般使⽤ setnx(set if not exists)指令,只允许被⼀个程序占有,使⽤完调⽤ del 释放锁。

redis 分布式锁有什么缺陷?

redis 分布式锁不能解决超时的问题,分布式锁有⼀个超时时间,程序的执⾏如果超出了锁的超时时间
就会出现问题。

redis 如何做内存优化?

尽量使⽤ redis 的散列表,把相关的信息放到散列表⾥⾯存储,⽽不是把每个字段单独存储,这样可以
有效的减少内存使⽤。⽐如将 web 系统的⽤户对象,应该放到散列表⾥⾯再整体存储到 redis,⽽不是
把⽤户的姓名、年龄、密码、邮箱等字段分别设置 key 进⾏存储。

说⼀下 jvm 的主要组成部分?及其作⽤?

类加载器(ClassLoader)
运⾏时数据区(Runtime Data Area)
执⾏引擎(Execution Engine)
本地库接⼝(Native Interface)
组件的作⽤: ⾸先通过类加载器(ClassLoader)会把 java 代码转换成字节码,运⾏时数据区
(Runtime Data Area)再把字节码加载到内存中,⽽字节码⽂件只是 jvm 的⼀套指令集规范,并不能
直接交给底层操作系统去执⾏,因此需要特定的命令解析器执⾏引擎(Execution Engine),将字节码
翻译成底层系统指令,再交由 CPU 去执⾏,⽽这个过程中需要调⽤其他语⾔的本地库接⼝(Native
Interface)来实现整个程序的功能。

说⼀下 jvm 运⾏时数据区?

不同虚拟机的运⾏时数据区可能略微有所不同,但都会遵从 java 虚拟机规范, java 虚拟机规范规定的
区域分为以下 5 个部分:
程序计数器(Program Counter Register):当前线程所执⾏的字节码的⾏号指示器,字节码解析
器的⼯作是通过改变这个计数器的值,来选取下⼀条需要执⾏的字节码指令,分⽀、循环、跳转、
异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
Java 虚拟机栈(Java Virtual Machine Stacks):⽤于存储局部变量表、操作数栈、动态链接、⽅
法出⼝等信息;
本地⽅法栈(Native Method Stack):与虚拟机栈的作⽤是⼀样的,只不过虚拟机栈是服务 java
⽅法的,⽽本地⽅法栈是为虚拟机调⽤ Native ⽅法服务的;
Java 堆(Java Heap):java 虚拟机中内存最⼤的⼀块,是被所有线程共享的,⼏乎所有的对象实
例都在这⾥分配内存;
⽅法区(Methed Area):⽤于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代
码等数据

什么是双亲委派模型?

在介绍双亲委派模型之前先说下类加载器。对于任意⼀个类,都需要由加载它的类加载器和这个类本身
⼀同确⽴在 jvm 中的唯⼀性,每⼀个类加载器,都有⼀个独⽴的类名称空间。类加载器就是根据指定全
限定名称将 class ⽂件加载到 jvm 内存,然后再转化为 class 对象。
类加载器分类:
启动类加载器(Bootstrap ClassLoader),是虚拟机⾃身的⼀部分,⽤来加载JAVA_HOME/lib/⽬
录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
其他类加载器:
扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext⽬录或
java.ext.dirs系统变量指定的路径中的所有类库;
应⽤程序类加载器(Application ClassLoader)。负责加载⽤户类路径(classpath)上的指
定类库,我们可以直接使⽤这个类加载器。⼀般情况,如果我们没有⾃定义类加载器默认就
是⽤这个加载器。
双亲委派模型:如果⼀个类加载器收到了类加载的请求,它⾸先不会⾃⼰去加载这个类,⽽是把这个请
求委派给⽗类加载器去完成,每⼀层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启
动类加载器中,只有当⽗加载⽆法完成加载请求(它的搜索范围中没找到所需的类)时,⼦加载器才会
尝试去加载类。

双亲委派模型的好处

使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。 例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。
避免了多份同样字节码的加载,内存是宝贵的,没必要保存相同的两份 Class 对象,例如 System.out.println() ,实际我们需要一个 System 的 Class 对象,并且只需要一份,如果不使用委托机制,而是自己加载自己的,那么类 A 打印的时候就会加载一份 System 字节码,类 B 打印的时候又会加载一份 System 字节码。而使用委托机制就可以有效的避免这个问题。
image
image

说⼀下类装载的执⾏过程?

类装载分为以下 5 个步骤:
加载:根据查找路径找到相应的 class ⽂件然后导⼊;
检查:检查加载的 class ⽂件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引⽤替换成直接引⽤的过程。符号引⽤就理解为⼀个标示,⽽在直
接引⽤直接指向内存中的地址;
初始化:对静态变量和静态代码块执⾏初始化⼯作。

怎么判断对象是否可以被回收?

⼀般有两种⽅法来判断:
引⽤计数器:为每个对象创建⼀个引⽤计数,有对象引⽤时计数器 +1,引⽤被释放时计数 -1,当
计数器为 0 时就可以被回收。它有⼀个缺点不能解决循环引⽤的问题;
可达性分析:从 GC Roots 开始向下搜索,搜索所⾛过的路径称为引⽤链。当⼀个对象到 GC
Roots 没有任何引⽤链相连时,则证明此对象是可以被回收的。

java 中都有哪些引⽤类型?

强引⽤:发⽣ gc 的时候不会被回收。
软引⽤:有⽤但不是必须的对象,在发⽣内存溢出之前会被回收。
弱引⽤:有⽤但不是必须的对象,在下⼀次GC时会被回收。
虚引⽤(幽灵引⽤/幻影引⽤):⽆法通过虚引⽤获得对象,⽤ PhantomReference 实现虚引⽤,
虚引⽤的⽤途是在 gc 时返回⼀个通知。

说⼀下 jvm 有哪些垃圾回收算法?

标记-清除算法:标记⽆⽤对象,然后进⾏清除回收。缺点:效率不⾼,⽆法清除垃圾碎⽚。
标记-整理算法:标记⽆⽤对象,让所有存活的对象都向⼀端移动,然后直接清除掉端边界以外的
内存。
复制算法:按照容量划分⼆个⼤⼩相等的内存区域,当⼀块⽤完的时候将活着的对象复制到另⼀块
上,然后再把已使⽤的内存空间⼀次清理掉。缺点:内存使⽤率不⾼,只有原来的⼀半。
分代算法:根据对象存活周期的不同将内存划分为⼏块,⼀般是新⽣代和⽼年代,新⽣代基本采⽤
复制算法,⽼年代采⽤标记整理算法。

详细介绍⼀下 CMS 垃圾回收器?

CMS 是英⽂ Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾
回收器。对于要求服务器响应速度的应⽤上,这种垃圾回收器⾮常适合。在启动 jvm 的参数加上“-
XX:+UseConcMarkSweepGC”来指定使⽤ CMS 垃圾回收器。
CMS 使⽤的是标记-清除的算法实现的,所以在 gc 的时候回产⽣⼤量的内存碎⽚,当剩余内存不能满⾜
程序运⾏要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采⽤ Serial Old 回收器进⾏
垃圾清除,此时的性能将会被降低。

新⽣代垃圾回收器和⽼⽣代垃圾回收器都有哪些?有什么区别?

新⽣代回收器:Serial、ParNew、Parallel Scavenge
⽼年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1

简述分代垃圾回收器是怎么⼯作的?

分代回收器有两个分区:⽼⽣代和新⽣代,新⽣代默认的空间占⽐总空间的 1/3,⽼⽣代的默认占⽐是
2/3。
新⽣代使⽤的是复制算法,新⽣代⾥有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占
⽐是 8:1:1,它的执⾏流程如下:
把 Eden + From Survivor 存活的对象放⼊ To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From
Survivor。
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是
15)时,升级为⽼⽣代。⼤对象也会直接进⼊⽼⽣代。
⽼⽣代当空间占⽤到达某个值之后就会触发全局垃圾收回,⼀般使⽤标记整理的执⾏算法。以上这些循
环往复就构成了整个分代垃圾回收的整体执⾏流程。

说⼀下 jvm 调优的⼯具?

jdk ⾃带了很多监控⼯具,都位于 jdk 的 bin ⽬录下,其中最常⽤的是 jconsole 和 jvisualvm 这两款视
图监控⼯具。
jconsole:⽤于对 jvm 中的内存、线程和类等进⾏监控;
jvisualvm:jdk ⾃带的全能分析⼯具,可以分析:内存快照、线程快照、程序死锁、监控内存的变
化、gc 变化等。

常⽤的 jvm 调优的参数都有哪些?

-Xms2g:初始化推⼤⼩为 2g;
-Xmx2g:堆最⼤内存为 2g;
-XX:NewRatio=4:设置年轻的和⽼年代的内存⽐例为 1:4;
-XX:SurvivorRatio=8:设置新⽣代 Eden 和 Survivor ⽐例为 8:2;
–XX:+UseParNewGC:指定使⽤ ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使⽤ ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使⽤ CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息

线程池的四个核心参数

核心线程数, 最大线程数, 非核心线程最大空闲时间keepaliveTime, 任务队列

Executors存在的问题

newFiexedThreadPool 使用队列LinkedBlockingQueue 链表无限长度 导致OOM 堆内存溢出,arrayBlockingQueue是定长队列 超过队列长度会采取拒绝策略
newCachedThreadPool 最大线程数为Integer最大值 导致OOM 无法继续创建本地线程错误

posted on 2022-03-07 16:31  kenchan14  阅读(46)  评论(0编辑  收藏  举报