面试1准备

102 阅读24分钟

java 基础 1.类加载的过程 加载 class ⽂件的⼆进制字节流读⼊内存 并在堆内存中为之创建 Class 对象,作为.class 进⼊内存后的数据的访问⼊⼝。 连接 验证 保证加载进来的字节流符合 JVM 的规范 例如校验符号引⽤是否可以通过全限 定名找到 准备 准备阶段的主要任务是为类变量开辟空间并赋默认值。 解析 该阶段的主要职责为将 Class 在常量池中的符号引⽤转变为直接引⽤ 初始化 该阶段主要是为类的类变量初始化值的,初始化有两种⽅式: 1、在声明类变量时,直接给变量赋值 2、在静态初始化块为类变量赋值 2.双亲委派模型 当⼀个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,⽽是把 这个请求委托给⾃⼰的⽗加载器去加载。只有⽗加载器⽆法加载这个类的时候,才 会由当前这个加载器来负责类的加载。 ● Bootstrap ClassLoader (启动类加载器),主要负责加载 Java 核⼼类库, %JRE_HOME%\lib 下的 rt.jar、resources.jar、charsets.jar 和 class 等。 ● Extention ClassLoader(扩展类加载器),主要负责加载⽬录 %JRE_HOME% \lib\ext ⽬录下的 jar 包和 class ⽂件。 ● Application ClassLoader(应⽤类加载器) ,主要负责加载当前应⽤的 classpath 下的所有类 ● User ClassLoader (⾃定义类加载器), ⽤户⾃定义的类加载器,可加载指定路 径的 class ⽂件. 破坏双亲委派机制 三次 如热替换 /JDBC/TOMCAT 3.synchronized 和 Reentranlock 区 synchronized JVM 层⾯的锁 通过 monitor 对象来完成(monitorenter 与 monitorexit) reentrantlock 通过利⽤ CAS(CompareAndSwap)⾃旋机制保证线程操作的原⼦性和 volatile 保 证数据可⻅性 是否可中断 synchronized 不可中断类型的锁,除⾮加锁的代码中出现异常或正常执⾏完成; ReentrantLock 可以中断,可通过 trylock(long timeout,TimeUnit unit) 设置超时⽅法 或者调⽤ interrupt ⽅法进⾏中断。 是否公平锁 synchronized 为⾮公平锁 ReentrantLock 则即可以选公平锁也可以选⾮公平锁,通 过构造⽅法 new ReentrantLock 时传⼊ boolean 值进⾏选择,为空默认 false ⾮公平 锁,true 为公平锁 4.currenthashmap 为什么是线程安全的 JDK1.7 ⾥⾯,ConcurrentHashMap 是⽤ Segment 和 HashEntry 实现的,每个 Segment 都是继承于 Reentrantlock 的,在对该 segment 进⾏操作时,获取锁,结 束操作释放锁。 具体来说,每⼀个 Segment 就相当于是⼀个⼩的 HashTable,内部包含了多个 HashEntry。⽽ Segment 通过将整个 HashTable 分成多个⼩的 Segment,并使⽤ ReentrantLock 对数据进⾏同步,来提⾼并发性。⽽ HashEntry 则是哈希表中的⼀ 个节点,包含了 key、value 以及⼀个 next 指针,⽤于形成链表结构。每个 HashEntry 的 next 指针指向下⼀个 HashEntry,从⽽形成了⼀个单链表 JDK1.8 ⾥⾯,没有⽤ segment,⽽是⽤ Node+CAS+synchronized 实现的。 JDK1.8 ⾸先,这段代码通过 spread ⽅法计算出 key 的哈希值,并根据此值找到对应的存储 桶。如果该桶为空,就调⽤ casTabAt ⽅法使⽤ CAS 操作直接插⼊新的节点;如果该 桶⾮空,就通过 synchronized 同步块确保在多线程情况下安全地更新已有的键值 对。 在更新已有键值对时,该⽅法会先检查该节点的哈希值是否与要插⼊的节点相同, 如果是就直接将新值赋给旧值;否则,就遍历链表或树中的所有节点,寻找是否已 有相同的 key 值,如果有,则更新其值;否则,使⽤ CAS 操作将新的节点插⼊到链 表或树的头部。同时,该⽅法还需要考虑到特殊情况如扩容、树化等,并在插⼊或 更新成功后检查是否需要执⾏扩容操作。 5.currenthashmap 链表转红⿊树的时机 1.当链表⻓度⼤于或等于阈值 TREEIFY_THRESHOLD(默认为 8)的时候,如果同 时还满⾜容量 (数组的⻓度) ⼤于或等于 MIN_TREEIFY_CAPACITY(默认为 64)的 要求,就会把链表转换为红⿊树 否则调⽤ resize() ⽅法进⾏扩容 2.当红⿊树中的元素数量⼩于等于 6 时,树就会被退化成链表 6.JDK1.8 put 的流程 1. 根据 key 计算出 hash 值和数组索引值,获取该位置上的节点(如果该节点不为 null)或锁对象(如果该节点为 null)。 2. 如果该节点不为 null,则遍历该节点对应的链表或红⿊树,查找是否已经存在 key 值相同的节点,如果存在,则更新其 value 并返回旧的 value;否则将新节点插 ⼊到链表或红⿊树中。 3. 如果该节点为 null,则需要使⽤ synchronized 等⽅式获取该数组位置上的锁对 象。如果多个线程同时需要获取该锁对象,则会采⽤⾃旋⽅式进⾏等待,直到某个 线程成功获取该锁对象为⽌。 4. 获取锁对象之后,再次判断该节点是否为 null。 5. 如果该节点仍为 null,则直接插⼊新节点。 6. 否则,如果该节点为 MOVED 标志,则需要帮助进⾏扩容操作。此时会调⽤ helpTransfer ⽅法来协助扩容。 7. 如果该节点已经被删除,则将其替换为新节点。 8. 如果该节点的 hash 值等于 MOVED 标志,则表示该节点正在被转移,此时将当 前操作加⼊到转移任务队列中,并阻塞当前线程,等待转移任务完成。 9. 如果该节点对应的链表⻓度⼤于等于 TREEIFY_THRESHOLD(8),且该节点还 没有被转换为红⿊树,则将该链表转换为红⿊树。 10. 如果该节点已经被转换为红⿊树,则使⽤红⿊树的操作进⾏插⼊。 11. 遍历该节点对应的链表或红⿊树,查找是否已经存在 key 值相同的节点。如果存 在,更新其 value 并返回旧的 value;否则将新节点插⼊到链表或红⿊树中。 synchronized 7.synchronized 锁升级过程 1.检测 Mark Word ⾥⾯是不是当前线程的 ID,如果是,表示当前线程处于偏向锁 2.如果不是,则使⽤ CAS 将当前线程的 ID 替换 Mard Word,如果成功则表示当前线 程获得偏向锁,置偏向标志位 1 3.如果失败,则说明发⽣竞争,撤销偏向锁,进⽽升级为轻量级锁。 4.当前线程使⽤ CAS 将对象头的 Mark Word 替换为锁记录指针,如果成功,当前线 程获得锁 5.如果失败,表示其他线程竞争锁,当前线程便尝试使⽤⾃旋来获取锁。 6.如果⾃旋成功则依然处于轻量级状态。 7.如果⾃旋失败,则升级为重量级锁。 8 采⽤的是⾃适应⾃旋锁来做的 1.8 默认⾃旋次数 (-XX:PreBlockSpin) 初始值是 10 ⾃旋次数最⼤值 (-XX:MaxSpins) 是 0 1.8 之前-XX:MaxSpin 参数控制的是⾃旋 次数的最⼤值,它的默认值是 10 8.synchronized 是可重⼊锁,那么它是如何实现可重⼊的呢? 偏向锁:检查 markWord 中的线程 ID 是否是当前线程,如果是的话就获取锁,继续 执⾏代码; 轻量级锁:检查 markWord 中指向 lockRecord 的指针是否是指向当前线程的 lockRecord,是的话继续执⾏代码; 重量级锁:检查_owner 属性,如果该属性指向了本线程,_count 属性 +1,并继续 执⾏代码。 www.bmabk.com/index.php/p… 9 类锁和对象锁 synchronized 类锁 . synchronized 加在 static ⽅法上(静态⽅法锁)。 . synchronized(*.class) 代码块。 对象锁 同步代码块锁 ⽅法锁 10 线程之间的通信⽅式 共享变量 / 等待 通知机制 / 阻塞队列 /Condition 条件 等待 通知机制—Java 提供了 Object 类的 wait()、notify() 和 notifyAll() Condition 条件—通过 Lock 对象的 newCondition() ⽅法获取⼀个 Condition 实例, 然后线程可以调⽤ await() ⽅法进⼊等待状态,直到其他线程调⽤ signal() 或 signalAll() ⽅法来唤醒它们 阻塞队列—阻塞队列 11.threadlocal 内存泄漏 原因 每个 thread 中都存在⼀个 map, map 的类型是 ThreadLocal.ThreadLocalMap. Map 中的 key 为⼀个 threadlocal 实例. 这个 Map 的确使⽤了弱引⽤,不过弱引⽤只是针 对 key. 每个 key 都弱引⽤指向 threadlocal. 当把 threadlocal 实例置为 null 以后,没有 任何强引⽤指向 threadlocal 实例,所以 threadlocal 将会被 gc 回收. 但是,我们的 value 却不能回收,因为存在⼀条从 current thread 连接过来的强引⽤. 只有当前 thread 结束以后, current thread 就不会存在栈中,强引⽤断开, Current Thread, Map, value 将全部被 GC 回收。所以得出⼀个结论就是只要这个线程对象被 gc 回 收,就不会出现内存泄露,但在 threadLocal 设为 null 和线程结束这段时间不会被回 收的,就发⽣了我们认为的内存泄露。 如何避免 1.在使⽤完 ThreadLocal 变量后,应该尽快调⽤ remove() ⽅法将其从 ThreadLocalMap 中清除,以便让垃圾回收器回收它们。 2. remove 的时候都会清除此线程 ThreadLocalMap ⾥ Entry 数组中所有 Key 为 null 的 Value 3.将 ThreadLocal 变量定义成 private static 类型的,并且在使⽤完之后⼿动清除, 以避免线程重⽤时引起的内存泄漏问题。 12.cglib 和 jdk 区别 JDK 动态代理只能代理实现了接⼝的类的⽅法,它通过反射来实现代理 /JDK 代理为什么⼀定需要实现接⼝—⽣成的代理对象默认继承了 Proxy 类,由于 Java 单继承特性,不能再继承另⼀个类,所以 只能实现接⼝。 cglib 在运⾏时动态⽣成⼀个要代理类的⼦类,重载其中的⽅法,从⽽实现对⽬标对 象的代理 13.设计模式 策略模式 / 单例模式 14. Redis常⻅命令 . Redis 常⻅数据结构 String:字符串类型 List:列表类型 Set:⽆序集合类型 ZSet:有序集合类型 Hash:哈希表类型 . 持久化机制 RDB 持久化是基于快照的持久化,把当前时刻全量数据持久化到磁盘上,最终⽣成⼀个 RBD ⽂件 redis.conf # 900s 内⾄少有⼀次写操作 save 900 1 AOF Redis 将所有的写操作命令追加到⼀个 AOF ⽂件中 持久化⽅式有三种 • always:每次写操作都同步到磁盘,保证最⾼的数据安全性,但性能较差。 ● everysec:每秒同步⼀次磁盘,提供较好的数据安全性和性能平衡。 ● no:由操作系统决定何时同步磁盘,性能最好,但数据安全性较差 # 开启 aof 持久化 appendonly yes # aof ⽂件名 appendfilename "appendonly.aof" # 持久化策略,always 表示每次写⼊都进⾏持久化 appendfsync always . Redis 快的原因 1) 使⽤内存存储数据,避免了磁盘 IO 的开销,提⾼了数据访问的速度。 2) 单线程模型,1.避免了多线程之间的上下⽂切换和竞争条件,提升 CPU 利⽤率。 2.可以保证数据的⼀致性和原⼦性。 3) ⾮阻塞 IO 多路复⽤机制 多路 I/O 复⽤模型是指使⽤⼀个线程来监控多个⽂件描述符(fd)的读写状态,当 某个 fd 准备好执⾏读或写操作时,就通知相应的事件处理器来处理。 这样就避免了阻塞式 I/O 模型中,单个线程只能等待⼀个 fd 的问题,提⾼了 I/O 效 率和利⽤率。 4.布隆过滤器 当⼀个元素被加⼊集合时,通过 K 个散列函数将这个元素映射成⼀个位数组中的 K 个点,把它们置为 1。 检索时,我们只 要看看这些点是不是都是 1 就 知道集合中有没有它了:如果这些 点有任何⼀个 0,则被检元素⼀定不在; 5.缓存淘汰策略 / lru/lfu/random/ volatile-ttl 默认策略,不删除任何 key,在进⾏写操作时返回错误信息 lru -优先删除最近最少使⽤ (least recently used ,LRU) 的 key 范围 volatile(只对设置了 expire 过期时间的 key ⽣效) allkeys 所有 key 通⽤; lfu-优先删除最不常⽤ (least frequently used ,LFU) 的 key ( Redis4.0)/volatile/ allkeys random-随机 /volatile/allkeys volatile-ttl 只对设置了 expire 过期时间的 key ⽣效,优先删除剩余时间 (time to live,TTL) 最短的 key 6.⼤ KEY 怎么办 ⼤ KEY 定义 . string 类型的 key 超过 10KB . hash/set/zset/list 等数据结构中元素个数⼤于 5k/ 整体占⽤内存⼤于 10MB 发现 Redis4.0 bigkeys 返回值 . 键名称(key name) . 类型(type) . 占⽤内存⼤⼩(size) 处理 . ⼤ key ⾮热 key,如果不是必要的信息,可以直接删除 del 或者 unlink 都可以。 如果是 redis4.0 之前的版本,建议对于 key 使⽤ (scan/sscan/hscan/zscan),将⼤ key 逐步删除(ltrim/zremrangebyscore/hdel/srem)。redis4.0 之后,直接使⽤ unlink 替换 del,会有后台线程将⼤ key 异步删除。 . 业务拆分,将 key 的含义更细粒度化,避免⼤ key 出现。 . 数据结构上拆分。如果⼤ key 是个⼤ json,可以通过 mset 的⽅式,将这个 key 的内容打散到各个实例中,减⼩⼤ key 对数据量倾斜的影响;如果是⼤ list,可 以拆成 list_1,list_2,list_N;其他数据结构同理。(可以考虑增加单独 key 存储 ⼤ key 被拆分的个数或元数据信息) . 在 redis 没有开启⾮同步删除机制的场景下,设置过期时间时,⼀定要避免⼤批 量键同时过期的现象,所以如果有这种情况,最好给过期时间加个随机范围, 缓解⼤量键同时过期,造成客户端等待超时的现象。 . 对于⻓⽂本,更建议使⽤⽂档型数据库例如 MongoDB 等。 7.选举机制 MySql 1.基础结构 erver 层和存储引擎层 Server 层包括连接器、查询缓存、分析器、优化器、执⾏器等 存储引擎: 主要负责数据的存储和读取,采⽤可以替换的插件式架构,⽀持 InnoDB、MyISAM、Memory 等多个存储引擎, 幻读的解决(两次查询的结果不同 快照读 (普通 select 语句 实现的⽅式是开始事务后(执⾏ begin 语句后),在执⾏第⼀个查询语句后,会创 建⼀个 Read View,后续的查询语句利⽤这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据 都是⼀样的,即使中途有其他事务插⼊了新纪录,是查询不出来这条数据的,所以 就很好了避免幻读问题。 针对当前读(select ... for update update/delete 等语句),是通过 next-key lock(记录锁 + 间隙锁)⽅式解决了幻读,因为当执⾏ select ... for update 语句的 时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插⼊了⼀ 条记录,那么这个插⼊语句就会被阻塞,⽆法成功插⼊,所以就很好了避免幻读问 题。 2.索引类型 普通索引 / 唯⼀索引 / 主键索引 / 组合索引 / 全⽂索引 3.联合索引的底层原理 (关键词) b+ 树 做回表 满⾜最左匹配原则 4.怎么判断有没有⾛索引 key:显示 MySQL 实际决定使⽤的键(索引)。如果没有选择索引,键是 NULL 5.explain 的参数 select_type:表示查询的类型,例如 SIMPLE 表示简单 SELECT 查询, SUBQUERY 表示使⽤⼦查询查询,UNION 表示 UNION 查询等。 table:表示所查询的表名。 partitions:表示 MySQL 是否将此查询优化为在特定的分区中执⾏。 type:表示 MySQL 在表中查找数据的⽅式,从最好到最差依次是 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。 属性值 含义 all 扫描全表数据 index 遍历索引 range 索引范围查找 fulltext 使⽤全⽂索引 possible_keys:表示 MySQL 可能使⽤的索引。 key:表示 MySQL 实际使⽤的索引。 key_len:表示 MySQL 在索引中使⽤的字节数,这对于 MySQL 的性能⾮常重要。 ref:表示 MySQL 在使⽤索引时需要参考的列。 rows:表示 MySQL 预计需要扫描的⾏数。 Extra:表示是否使⽤了临时表、是否使⽤了索引扫描等其他信息。 6.常⻅的⽇志类型 Innodb 存储引擎层⽣成的⽇志 undo log :原⼦性 /MVCC ⼀条记录的每⼀次更新操作产⽣的 undo log 格式都有⼀个 roll_pointer 指针和⼀ 个 trx_id 事务 id: ● 通过 trx_id 可以知道该记录是被哪个事务修改的; ● 通过 roll_pointer 指针可以将这些 undo log 串成⼀个链表,这个链表就被称 为版本链; 两⼤作⽤ ● 实现事务回滚,保障事务的原⼦性。事务处理过程中,如果出现了错误或者⽤ 户执 ⾏了 ROLLBACK 语句,MySQL 可以利⽤ undo log 中的历史数据将数 据恢复到事务开始之前的状态。 ● 实现 MVCC(多版本并发控制)关键因素之⼀。MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执⾏ 快照读(普通 select 语句)的时候,会根据事务的 Read View ⾥的信息,顺 着 undo log 的版本链找到满⾜其可⻅性的记录。 redo log : redo log 是物理⽇志,记录了某个数据⻚做了什么修改,实现了事务中的持久性, 主要⽤于掉电等故障恢复; 崩溃恢复时可以根据 redo log 的内容,将所有数据恢复到最新的状态。 每当产⽣⼀条 redo log 时,会先写⼊到 redo log buffer, redo log 什么时候刷盘 ● MySQL 正常关闭时; ● 当 redo log buffer 中记录的写⼊量⼤于 redo log buffer 内存空间的⼀半时, 会触发落盘; ● InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。 ● 每次事务提交时都将缓存在 redo log buffer ⾥的 redo log 直接持久化到磁 盘。 Server 层实现的⽇志 binlog ⽤于复制,在主从复制中,从库利⽤主库上的 binlog 进⾏重播,实现主从同 步。 2,⽤于数据库的基于时间点的还原。 7.索引失效的情况 联合索引不满⾜最左匹配原则 select * 索引列参与运算 explain select * from t_user where id + 1 = 2 ; ● ⽅式⼀:like '%abc'; ● ⽅式三:like '%abc%'; 其中⽅式⼀和⽅式三,由于占位符出现在⾸部,导致⽆法⾛索引。索引本身就相当 于⽬录,从左到右逐个排序。⽽条件的左侧使⽤了占位符,导致⽆法按照正常的⽬ 录进⾏匹配,导致索引失效就很正常了 .类型隐式转换 explain select * from t_user where id_no = 1002; id_no 字段类型为 varchar,但在 SQL 语句中使⽤了 int 类型,导致全表扫描。 OR/ ⼤于 / ⼩于 8.sql 优化 语句优化 ● 使⽤正确的索引 . 我们应该尽可能的使⽤主键查询,⽽⾮其他索引查询,因为主键查询不会触发 回表查询,因此节省了⼀部分时间,变相的提⾼了查询的性能。 . 避免在 where 查询条件中使⽤ != 或者 <> 操作符,因为这些操作符会导致查 询引擎放弃索引⽽进⾏全表扫描。 . 适当使⽤前缀索引,MySQL 是⽀持前缀索引的,也就是说我们可以定义字符串 的⼀部分来作为索引。我们知道索引越⻓占⽤的磁盘空间就越⼤,那么在相同 数据⻚中能放下的索引值也就越少,这就意味着搜索索引需要的查询时间也就 越⻓,进⽽查询的效率就会降低,所以我们可以适当的选择使⽤前缀索引,以 减少空间的占⽤和提⾼查询效率。⽐如,邮箱的后缀都是固定的 “@xxx.com”,那么类似这种后⾯⼏位为固定值的字段就⾮常适合定义为前缀 索引。 ● 查询具体的字段⽽⾮全部字段 . 要尽量避免使⽤ select *,⽽是查询需要的字段,这样可以提升速度,以及减 少⽹络传输的带宽压⼒。 ● 优化⼦查询 . 尽量使⽤ Join 语句来替代⼦查询,因为⼦查询是嵌套查询,⽽嵌套查询会新创 建⼀张临时表,⽽临时表的创建与销毁会占⽤⼀定的系统资源以及花费⼀定的 时间,但 Join 语句并不会创建临时表,因此性能会更⾼。 ● 注意查询结果集 我们要尽量使⽤⼩表驱动⼤表的⽅式进⾏查询,也就是如果 B 表的数据⼩于 A 表的 数据,那执⾏的顺序就是先查 B 表再查 A 表,具体查询语句如下: select name from A where id in (select id from B); ● 不要在列上进⾏运算操作 不要在列字段上进⾏算术运算或其他表达式运算,否则可能会导致查询引擎⽆法正 确使⽤索引,从⽽影响了查询的效率。 ● 适当增加冗余字段 增加冗余字段可以减少⼤量的连表查询,因为多张表的连表查询性能很低,所有可 以适当的增加冗余字段,以减少多张表的关联查询,这是以空间换时间的优化策略。 数据库结构优化 ● 最⼩数据⻓度 ⼀般说来数据库的表越⼩,那么它的查询速度就越快,因此为了提⾼表的效率,应 该将表的字段设置的尽可能⼩,⽐如身份证号,可以设置为 char(18) 就不要设置为 varchar(18)。 ● 使⽤最简单数据类型 能使⽤ int 类型就不要使⽤ varchar 类型,因为 int 类型⽐ varchar 类型的查询效率 更⾼。 ● 尽量少定义 text 类型 text 类型的查询效率很低,如果必须要使⽤ text 定义字段,可以把此字段分离成⼦ 表,需要查询此字段时使⽤联合查询,这样可以提⾼主表的查询效率。 适当分表、分库策略 分表和分库⽅案也是我们经常说的垂直分隔(分表)和⽔平分隔(分库)。 分表是指当⼀张表中的字段更多时,可以尝试将⼀张⼤表拆分为多张⼦表,把使⽤ ⽐较⾼频的主信息放⼊主表中,其他的放⼊⼦表,这样我们⼤部分查询只需要查询 字段更少的主表就可以完成了,从⽽有效的提⾼了查询的效率。 分库是指将⼀个数据库分为多个数据库。⽐如我们把⼀个数据库拆分为了多个数据 库,⼀个主数据库⽤于写⼊和修改数据,其他的⽤于同步主数据并提供给客户端查 询,这样就把⼀个库的读和写的压⼒,分摊给了多个库,从⽽提⾼了数据库整体的 运⾏效率。 ● 9.事务特性 1、原⼦性(Atomicity) 2、⼀致性(Consistency) 3、隔离性(Isolation) 4、持久性(Durability) 10.union 和 union all 区别 union 去重并排序,union all 直接返回合并的结果,不去重也不排序; Mybatis 1.⼀级缓存和⼆级缓存的区别 ● ⼀级缓存的作⽤域是⼀个 sqlsession 内;Myabits 默认开启⼀级缓存。 ● ⼆级缓存作⽤域是针对 mapper 进⾏缓存; Mybatis ⼆级缓存是默认不开启 的,作⽤于⼀个 Application,是 Mapper 级别的,多个 SqlSession 使⽤同 ⼀个 Mapper 的 sql 能够使⽤⼆级缓存。 2.防⽌ sql 注⼊ ⽤#{} 3.分⻚⽅式 原⽣ Limit 分⻚ PageHelper 分⻚插件 int currPage = 2;// 当前⻚码 int pageSize = 3;// 当前⻚记录数量 // 表示获取第 2 ⻚,3 条内容,默认会查询总数 count PageHelper.startPage(currPage,pageSize); Spring ⾯试题 1.Autowired 和 Resource 区别 @Autowired 注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对 象必须存在, 如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使⽤按照名称 (byName)来装配, 可以结合 @Qualifier 注解⼀起使⽤。 @Resource 装配顺序: ① 如果同时指定了 name 和 type, 2.⾃动装配 通过⾃定义实现 ImportSelector 接⼝,然后通过 SpringFactoriesLoader 扫描 autoconfigure 包下的 META-INF/spring.factories 中所有路径下的类, 并通过反射实例化成⼀个个的配置类并注⼊到 Spring 容器中,从⽽实现了⾃动装配 3.springboot ⾃动装配 通过⾃定义实现 ImportSelector 接⼝,然后通过 SpringFactoriesLoader 扫描 autoconfigure 包下的 META-INF/spring.factories 中所有路径下的类, 并通过反射实例化成⼀个个的配置类并注⼊到 Spring 容器中,从⽽实现了⾃动装配 4.bean ⽣命周期 5 循环依赖的解决 循环依赖主要是由于相互进⾏ setter 注⼊ ⽽构造注⼊不会报错 解决的办法发⽣实例化的过程 ⽐如 A 会调⽤ doGetbean ⽅法进⾏实例化 先从⼀级缓存进⾏查找 如果没有且 a 也不处于创建阶段 就会触发 A 的实例化 最后调⽤ doCreateBean ⽅法中的 addSingletonFactory ⽅法 将 A 和他的匿名内部 类放到三级缓存中 接下来会调⽤ populateBean ⽅法中的 applyPropertyValues 进⾏填充 bean 这时候 会调⽤ getsingleton 找 b 接下来类似于前⾯的步骤将 b 和他的匿名内部类放到三级缓存中 这时候 A 和 B 都在 三级缓存中 接下来 b 实例化时 会去缓存中找 A 并他从三级缓存移⼊⼆级缓存 b 在初始化化完成后 会从三级缓存进 ⼊⼀级缓存主要是通过 addSingleton ⽅法实现的 接下来就是初始化 a 并从⼆级缓存移⼊⼀级缓存 6spring 的传播机制 REQUIRED(Spring 默认的事务传播类型) 如果当前没有事务,则⾃⼰新建⼀个事 务,如果当前存在事务,则加⼊这个事务 SUPPORTS 当前存在事务,则加⼊当前事务,如果当前没有事务,就以⾮事务⽅法 执⾏ MANDATORY 当前存在事务,则加⼊当前事务,如果当前事务不存在,则抛出异 常。 REQUIRES_NEW 创建⼀个新事务,如果存在当前事务,则挂起该事务。 NOT_SUPPORTED 始终以⾮事务⽅式执⾏,如果当前存在事务,则挂起当前事务 NEVER 不使⽤事务,如果当前事务存在,则抛出异常 NESTED 如果当前事务存在,则在嵌套事务中执⾏,否则 REQUIRED 的操作⼀样 (开启⼀个事务) 线上问题排查 1.热替换 局限性 2 查看⽅法耗时 trace com.demo.controller.DemoController ⽅法名 耗时超过 1000 毫秒 watch com.example.arthas.controller.Test111Controller test1 "{params,returnObj}" "#cost>1000" -x 3 3.查看⽅法的⼊参和返回结果 Dubbo 常⻅⾯试题 Git 常⻅命令 1. 添加指定⽂件到暂存区 git add [file1] [file2] ... 2.提交暂存区的指定⽂件到仓库区 git commit [file1] [file2] ... -m [message] 3. # 列出所有本地分⽀和远程分⽀ git branch -a 4. # 切换到指定分⽀,并更新⼯作区 git checkout [branch-name] 5.# 合并指定分⽀到当前分⽀ git merge [branch] 6.# 删除分⽀ git branch -d [branch-name] 7.# 显示暂存区和⼯作区的差异 git diff 8.# 下载远程仓库的所有变动 git fetch [remote] 9.# 取回远程仓库的变化,并与本地分⽀合并 git pull [remote] [branch] # 上传本地指定分⽀到远程仓库 $ git push [remote] [branch] RocketMq 1.有哪些⻆⾊ ⽣产者 / 消费者 /Broker/NameServer 2.执⾏流程 . Broker 在启动的时候去向所有的 NameServer 注册,并保持⻓连接,每 30s 发送 ⼀次⼼跳 Producer 在发送消息的时候从 NameServer 获取 Broker 服务器地址,根据负载 均衡算法选择⼀个或多个 Message Queue 上进⾏消息的发送 ⼀旦消息进⼊了 Message Queue,RocketMQ 会将消息存储到磁盘上并对其进 ⾏索引,以⽀持快速查找和检索 消费者在订阅某个 Topic 时,会通过查询 NameServer 获取 Topic 所属的 Brokers,然后会连接到这些 Broker,并从它们消费消息。 RocketMQ ⽀持两种消息消费模式:集群消费和⼴播消费。 3.消息刷盘怎么实现的 同步 / 异步 同步刷盘机制。消息从 Producder 端发送出去后,被 Broker 接收,Broker 接收到 消息后将消息写⼊内存的 PageCache 后,⽴即通知刷盘线程进⾏刷盘,当前线程等 待刷盘线程的通知。刷盘线程开始进⾏刷盘操作,刷盘完毕后唤醒之前等待的线 程,再返回写成功状态,最后 Producer 会收到消息发送成功的 ACK。 异步刷盘 右图代表异步刷盘机制。消息从 Producder 端发送出去后,被 Broker 接收到, Broker 端接收到消息后,消息被写⼊ PageCache 后⽴即返回写成功给 Producer 端。然后另⼀个异步线程专⻔会将 PageCache 中的数据写到磁盘⾥,确保消息的持 久化。 4.顺序消费 JVM 1.内存区域如何划分 线程共享—⽅法区 / 堆 线程私有—程序计数器 / 虚拟机栈 / 本地⽅法栈 ● ● ● 程序计数器(Program Counter Register):每个线程都有⼀个程序计数器。 当线程执⾏ Java ⽅法时,程序计数器保存当前执⾏指令的地址,以便在 JVM 调⽤其他⽅法或恢复线程执⾏时重新回到正确的位置。 Java 虚拟机栈(Java Virtual Machine Stacks):每个线程都有⼀个虚拟机 栈。虚拟机栈保存着⽅法执⾏期间的局部变量、操作数栈、⽅法出⼝等信息。 线程每调⽤⼀个 Java ⽅法时,会创建⼀个栈帧(Stack Frame),栈帧包含 着该⽅法的局部变量、操作数栈、⽅法返回地址等信息。栈帧在⽅法执⾏结束 后会被弹出。 本地⽅法栈(Native Method Stack):与 Java 虚拟机栈类似,但是为本地 ● ● ● ⽅法服务。 Java 堆(Java Heap):Java 堆是 Java 虚拟机中最⼤的⼀块内存区域,⽤ 于存储各种类型的对象实例,也是垃圾收集器的主要⼯作区域。Java 堆是所有 线程共享的部分。 ⽅法区(Method Area):⽅法区也是所有线程共享的部分,它⽤于存储类的 加载信息、静态变量、常量池、⽅法字节码等数据。在 Java 8 及以前的版本 中,⽅法区被实现为永久代(Permanent Generation),在 Java 8 中被改为 元空间(Metaspace)。 2.对象的内存布局 对象头 / 实例数据 / 对⻬填充 ● 对象头(object header):包括了关于堆对象的布局、类型、GC 状态、同 步状态和标识哈希码的基本信息。Java 对象和 vm 内部对象都有⼀个共同的对 象头格式。 ● 实例数据(Instance Data):主要是存放类的数据信息,⽗类的信息,对象 字段属性信息。 ● 对⻬填充(Padding):为了字节对⻬,填充的数据,不是必须 3.如何判断对象还存活 / 对象需要回收 引⽤计数法算法 / 引⽤不可达算法 引⽤计数法算法 给每⼀个对象添加⼀个引⽤计数器,当⼀个引⽤指向对象,计数器值加⼀,当⼀个 引⽤失效,计数器减⼀ 引⽤不可达算法 通过 GC Roots 对象作为起始点,开始向下搜索,当⼀个对象到 GC Roots 没有任何 引⽤链相连时,则证明此对象时不可⽤。 GC Roots 1、虚拟机栈(栈帧中的局部变量表)中的引⽤的对象 。 2、⽅法区中类静态属性引⽤的对象。 3、⽅法区中常量引⽤的对象。 4、本地⽅法栈中 JNI(Native ⽅法)引⽤的对象。 4.垃圾回收算法有哪⼏种 标记-清除 / 标记复制 / 标记整理 标记清除是标记完需要回收的对象之后统⼀回收 ⽐较容易产⽣碎⽚问题 cms 收集器 就是这种想法实现的 ⽼年代⽤的这个 标记复制就是把划分出 AB 两个 把 A 中的存活对象复制到 B 中 之后把 A 空间⼀次清理 掉 新⽣代⽤的这个 标记整理就是标记完需要回收的对象之后 让所有存活的对象都向内存空间⼀端移 动,然后直接清理掉边界以外的内存 5.对象什么时候进⼊⽼年代 对象通常在 Eden 区⾥诞⽣,如果经过第⼀次 Minor(新⽣代) GC 后仍然存活, 并且能被 Survivor 容纳的话,该对象会被移动到 Survivor 空间中,并且将其对象 年 龄设为 1 岁。 对象在 Survivor 区中每熬过⼀次 Minor GC,年龄就增加 1 岁,当它的年龄增加到⼀ 定程 度(默认为 15),就会被晋升到⽼年代中。对象晋升⽼年代的年龄阈值,可以 通过参数-XX: MaxTenuringThreshold 设置 6.垃圾回收器有哪⼏种 Parallel 收集器标记-清除算法 /CMS 收集器标记-清除算法 /G1 分代式标记-整理算法 7.常⻅参数 说 堆⼤⼩ / 栈⼤⼩ /Dump ⽂件导出路径 1.-Xms:初始堆⼤⼩。只要启动,就占⽤的堆⼤⼩。 2.-Xmx:最⼤堆⼤⼩。java.lang.OutOfMemoryError:Java heap 这个错误可以通 过配置-Xms 和-Xmx 参数来设置。 3.-Xss:栈⼤⼩分配。栈是每个线程私有的区域,通常只有⼏百 K ⼤⼩,决定了函 数调⽤的深度,⽽局部变量、参数都分配到栈上。 当出现⼤量局部变量,递归时,会发⽣栈空间 OOM (java.lang.StackOverflowError)之类的错误。 4.XX:NewSize:设置新⽣代⼤⼩的绝对值。 5.-XX:NewRatio:设置年轻代和年⽼代的⽐值。⽐如设置为 3,则新⽣代:⽼年代 =1:3,新⽣代占总 heap 的 1/4。 6.-XX:MaxPermSize:设置持久代⼤⼩。 java.lang.OutOfMemoryError:PermGenspace 这个 OOM 错误需要合理调⼤ PermSize 和 MaxPermSize ⼤⼩。 7.-XX:SurvivorRatio:年轻代中 Eden 区与两个 Survivor 区的⽐值。注意,Survivor 区有 form 和 to 两个。⽐如设置为 8 时,那么 eden:form:to=8:1:1。 8.-XX:HeapDumpOnOutOfMemoryError:发⽣ OOM 时转储堆到⽂件,这是⼀个 ⾮常好的诊断⽅法。 9.-XX:HeapDumpPath:导出堆的转储⽂件路径。 8.类的加载过程 加载 通过类的全限定名(包名 + 类名),获取到该类的.class ⽂件的⼆进制字节流 将⼆进制字节流所代表的静态存储结构,转化为⽅法区运⾏时的数据结构 在内存中⽣成⼀个代表该类的 java.lang.Class 对象,作为⽅法区这个类的各种数据 的访问⼊⼝ 链接 验证—确保 class ⽂件中的字节流包含的信息,符合当前虚拟机的要求 准备—为类中的静态字段分配内存,并设置默认的初始值 解析—-是将常量池内的符号引⽤转换为直接引⽤的过程 初始化 执⾏类的构造器⽅法 init() 的过程 若该类具有⽗类,jvm 会保证⽗类的 init 先执⾏,然后在执⾏⼦类的 ini 9.CMS 和 G1 的区别 / 不清楚 场景问题 1.服务假死 top/jstack/jstat top 命令查看进程的 CPU 和内存占⽤情况 /jstack 命令⽣成 Java 进程的线程堆栈 信息 / jstat 命令查看 Java 进程的各项指标,如 GC 次数和时间、类加载数量 zhuanlan.zhihu.com/p/529350757 hongyitong.github.io/ 2018/01/18/%E5%88%86%E6%9E%90java%E8%BF%9B%E7%A8%8B%E5%8 1%87%E6%AD%BB%E6%83%85%E5%86%B5/ 2.缓存设计 guava