【JAVA面试精选篇-初生牛犊不怕虎】

152 阅读12分钟

🌽 简介

海阔凭鱼跃,天高任鸟飞! 学习不要盲目,让大脑舒服的方式吸收知识!!!

本人马上离开济南,回泰安发展,为了积极准备面试,目前在梳理一些知识点,同时希望能够帮助到需要的人......

🧺 线程池

线程池添加线程存放顺序顺序~ 线程池的创建~

自定义线程池->ThreadPoolExecutor(必会⭐⭐⭐⭐⭐)

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize 核心线程数 计算策略: CPU密集型->设置为CPU 核数 +1 IO密集型-> CPU 核心数 * (1 + IO 耗时/ CPU 耗时)
  • maximumPoolSize 最大线程数
  • keepAliveTime 生存时间
  • TimeUnit 时间单位
  • BlockingQueue (有界队列/无界队列)
  • ThreadFactory 线程工厂
  • RejectedExecutionHandler 拒绝策略

Executors 创建线程池(理解⭐⭐⭐)

Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待; Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程; Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序; Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池; Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池; Executors.newWorkStealingPool:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)

🌄 Redis

Redis经典剖析: 读完这篇,Redis面试基本过关!!!(掌握⭐⭐⭐⭐⭐)

⏰ JVM

jvm内存模型(掌握⭐⭐⭐⭐⭐)

方法区(元空间)

在程序中声明的常量、静态变量和有关于类的信息等的引用,都会存放在方法区,而这些引用所指向的具体对象 一般都会在堆中开辟单独的空间进行存储,也可能会在直接内存中进行存储。

堆区 通过new关键字创建的对象

对JVM的性能调优一般就是对堆内存的调优。

Java中基本类型的包装类: Byte、Short、Integer、Long、Float、Double、Boolean、Character类型的数据是存储在堆中的。

年轻代和老年代

在这里插入图片描述

基本数据类型(byte、short、int、long、float、double、boolean、char)和引用变量都是在栈中的。

线程退出或者方法退出时,栈中的数据会被自动清除

局部变量表、操作数栈、动态链接和方法出口

引用所指向的具体对象一般都会在堆中开辟单独的地址空间进行存储,也有可能存储在直接内存中。 在JVM中,如果开启了逃逸分析和标量替换,则可能不会再在堆上创建对象,可能会将对象直接分配到栈上,也可能不再创建对象,而是进一步分解对象中的成员变量,将其直接在栈上分配空间并赋值。

jvm优化机制-逃逸分析解读

类加载流程(掌握⭐⭐⭐⭐⭐)

在这里插入图片描述

【双亲委派加载机制】 先使用父加载器加载,如果父加载器找不到要加载的目标类,就使用子加载器自己加载。 使用双亲委派机制,能够防止JVM内存中出现多份相同的字节码。

【加载过程】

  • 加载: 主要是在计算机磁盘上通过IO流读取字节码文件(.class文件),当程序需要使用某个类时,才会对这个类进行加载操作,比如,在程序中调用某个类的静态方法,使用new关键字创建某个类的对象等。在加载阶段,往往会在JVM的堆内存中生成一个代表这个类的Class对象,这个对象作为存放在JVM方法区中这个类的各种数据的访问入口,也可以叫做访问句柄。
  • 验证:主要的作用就是校验字节码的正确性,是否符合JVM规范。
  • 准备:为类的静态变量分配相应的内存,并赋予默认值。
  • 解析:将程序中的符号引用替换为直接引用,这里的符号引用包括:静态方法等。此阶段就是将一些静态方法等符号引用替换成指向数据所在内存地址的指针,这些指针就是直接引用。如果是在类加载过程中完成的符号引用到直接引用的替换,这个替换的过程就叫作静态链接过程。如果是在运行期间完成的符号引用到直接引用的替换,这个替换的过程就叫作动态链接过程。
  • 初始化:对类的静态变量进行初始化,为其赋予程序中指定的值,并执行静态代码块中的代码。

在准备阶段和初始化阶段都会为类的静态变量赋值

  • 在准备阶段为类的静态变量赋予的是默认值
  • 在初始化阶段为类的静态变量赋予的是真正要赋予的值

JVM调优(掌握⭐⭐⭐⭐⭐)

在JVM中,主要是对堆(新生代)、方法区和栈进行性能调优

  • 堆:-Xms、-Xmx
  • 新生代:-Xmn
  • 方法区(元空间):-XX:MetaspaceSize、-XX:MaxMetaspaceSize
  • 栈(线程):-Xss

在设置JVM启动参数时,需要特别注意方法区(元空间)的参数设置

  • -XX:MetaspaceSize: 指的是方法区(元空间)触发Full GC的初始内存大小(方法区没有固定的初始内存大小),以字节为单位,默认为21M。达到设置的值时,会触发Full GC,同时垃圾收集器会对这个值进行修改。 --XX:MaxMetaspaceSize: 指的是方法区(元空间)的最大值,默认值为-1,不受堆内存大小限制,此时,只会受限于本地内存大小。

调整方法区(元空间)的大小会发生Full GC,这种操作的代价是非常昂贵的。如果发现应用在启动的时候发生了Full GC,则很有可能是方法区(元空间)的大小被动态调整了。

为了尽量不让JVM动态调整方法区(元空间)的大小造成频繁的Full GC 一般将-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置成一样的值 物理内存8G,可以将这两个值设置为256M

调优经验值

物理内存8G的机器调优推荐参数

  • 启动springBoot
java ‐Xms2048M 
‐Xmx2048M ‐Xmn1024M 
‐Xss512K ‐XX:MetaspaceSize=256M 
‐XX:MaxMetaspaceSize=256M ‐jar xxx.jar
  • 启动Tomcat(Linux) 在Tomcat bin目录下catalina.sh文件里配置
‐Xms2048M ‐Xmx2048M 
‐Xmn1024M ‐Xss512K 
‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
  • 启动Tomcat(Windows) 在Tomcat bin目录下catalina.bat文件里配置
‐Xms2048M ‐Xmx2048M 
‐Xmn1024M ‐Xss512K 
‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M

Xms和Xmx设为相同值也是 Oracle 官方推荐的,会减少 GC 的操作,也意味着只有当 JVM 即将使用完时才会进行回收,此前内存会不停的增长。

锁(掌握⭐⭐⭐⭐⭐)

锁名称应用
乐观锁CAS
悲观锁synchronized、vector、hashtable
自旋锁CAS
可重入锁synchronized、Reentrantlock、Lock
读写锁ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
公平锁Reentrantlock(true)
非公平锁synchronized、reentrantlock(false)
共享锁ReentrantReadWriteLock中读锁
独占锁synchronized、vector、hashtable、ReentrantReadWriteLock中写锁
重量级锁synchronized
轻量级锁锁优化技术
偏向锁锁优化技术
分段锁concurrentHashMap
互斥锁synchronized
同步锁synchronized
死锁相互请求对方的资源
锁粗化锁优化技术
锁消除锁优化技术

🚛 数据结构

集合图解(掌握⭐⭐⭐⭐⭐)

HashMap(掌握⭐⭐⭐⭐⭐) PUT流程

hashtable、concuurentHashMap、TreeMap、HashMap的区别

hashMap为什么出现死循环???

HashMap 在JDK1.7和1.8中的区别

ConcurrentHashMap在jdk1.7和jdk1.8中的不同

🍎 Mysql

逻辑结构(理解⭐⭐⭐)

存储引擎执行流程(掌握⭐⭐⭐⭐⭐)

b+Tree(掌握⭐⭐⭐⭐⭐)

  • 减少磁盘IO
  • 提高范围查询

mysql优化(精通⭐⭐⭐⭐⭐⭐)

SQL执行顺序~

字段

  • 尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED
  • VARCHAR的长度只分配真正需要的空间
  • 使用枚举或整数代替字符串类型
  • 尽量使用TIMESTAMP而非DATETIME,
  • 单表不要有太多字段,建议在20以内
  • 避免使用NULL字段,很难查询优化且占用额外索引空间
  • 用整型来存IP
  • 使用自增主键

索引

  • 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
  • 应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段
  • 字符字段只建前缀索引
  • 字符字段最好不要做主键
  • 不用外键,由程序保证约束
  • 尽量不用UNIQUE,由程序保证约束
  • 使用多列索引时注意顺序和查询条件保持一致,同时删除不必要的单列索引

查询SQL

  • 可通过开启慢查询日志来找出较慢的SQL
  • 不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等
  • 查询时要尽可能将操作移至等号右边
  • sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库
  • 不用SELECT *
  • OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内
  • 不用函数和触发器,在应用程序实现
  • 避免%xxx式查询
  • 少用JOIN
  • 使用同类型进行比较,比如用'123'和'123'比,123和123比
  • 尽量避免在WHERE子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
  • 对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
  • 列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大
  • 尽量用union all代替union
  • IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况

进阶优化

使用连接池
set global max_connections=200;
利用缓存机制
SET GLOBAL query_cache_size = 1048576;
使用连接缓存
SET GLOBAL thread_cache_size = 10;
合理选择事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
设置合适的死锁超时时间
SET GLOBAL innodb_deadlock_detect_interval = 10;
事务日志优化
SET GLOBAL innodb_log_file_size = 100M;
合理使用慢查询日志
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;
使用连接池预热
SET GLOBAL thread_pool_size = 10;

系统参数设置:
wait_timeout:数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的8小时减到半小时
max_user_connection:最大连接数,默认为0无上限,最好设一个合理上限thread_concurrency:并发线程数,设为CPU核数的两倍
skip_name_resolve:禁止对外部连接进行DNS解析,消除DNS解析时间,但需要所有远程主机用IP访问
key_buffer_size:索引块的缓存大小,增加会提升索引处理速度,对MyISAM表性能影响最大。对于内存4G左右,可设为256M或384M,通过查询show status like'key_read%',保证key_reads / key_read_requests在0.1%以下最好
innodb_buffer_pool_size:缓存数据块和索引块,对InnoDB表性能影响最大。通过查询show status like 'Innodb_buffer_pool_read%',保证 (Innodb_buffer_pool_read_requests– Innodb_buffer_pool_reads)/ Innodb_buffer_pool_read_requests越高越好
innodb_additional_mem_pool_size:InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL会记录Warning信息到数据库的错误日志中,这时就需要该调整这个参数大小
innodb_log_buffer_size:InnoDB存储引擎的事务日志所使用的缓冲区,一般来说不建议超过32MB

硬件优化 根据 MySQL 是 CPU 密集型还是 I/O 密集型,通过提升 CPU 和内存、使用 SSD 等方式能显著提升 MySQL 性能 mysql 是关系型数据库,适合垂直升级

读写分离 缓存

系统内部调整相关缓存参数 针对前端 web 页面做缓存 mysql 前置缓存服务器(redis)

分库分表

mvcc(理解⭐⭐⭐⭐⭐⭐)

原理分析

  • 获取事务自己事务ID,即trx_id。(这个也不是select的时候获取的,而是这个事务开启的时候获取的 也就是begin的时候)
  • 获取ReadView(这个才是select的时候才会生成的)
  • 数据库表中如果查询到数据,那就到ReadView中的事务版本号进行比较。
  • 如果不符合ReadView的可见性规则, 即就需要Undo log中历史快照,直到返回符合规则的数据;

InnoDB 实现MVCC,是通过ReadView+ Undo Log 实现的,Undo Log 保存了历史快照,ReadView可见性规则帮助判断当前版本的数据是否可见。

ReadView可见性规则

  • RC隔离级别->每个select都会创建最新的ReadView
  • RR隔离级别->当事务中的第一个select请求才创建ReadView

🍡 结语

本次内容梳理了一天,整体梳理下来对jvm,mysql,多线程,数据结构,redis理解更深刻了,后续会增加针对微服务,中间件的分析,目的在于强化记忆,理解原理,做个面试不倒翁!