面试必备问题汇总

Java基础篇

数据结构

1、数组、链表、散列表、二叉树的区别?

数组是连续的内存空间,有下标值index,根据下标寻址快。往数组中插入或删除元素会造成数组重构,效率低下。用于已知的数据量。 链表是一个节点指向下一个节点,寻址较慢,添加、移除数据较方便。在频繁插入和删除的数据中,链表有很大的优势 。 哈希表(散列表)是数组跟链表的结合体,体现了空间换时间的概念。结合散列函数可以确定元素在数组中哈希桶的位置,然后由哈希冲突决定最终的寻址复杂度。利用负载因子跟扩容机制保证综合效率。哈希表结合了数组跟链表,所以综合性能较优。 二叉树一般用来查找,即搜索二叉树,可以保证搜索元素的时间复杂度在 O(logn) 以内。二叉树遵循右子树大于根节点,左子树小于根节点的原则进行数据的插入和保存。二叉树能够保证有序存储(按照具体的值进行比较),比较适合用于对数据进行排序存储。查询、新增元素效率在 优化树 的结构里综合效率不错。

备注:在大多数计算机书上log(n)就是以2为底的对数,之所以出现log(n),多半是二分;

2、栈、队列的区别?

栈先进后出,栈顶进入(压栈),栈顶出(弹出、出栈);栈就是一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来。(后进先出) 队列先进先出,一端插入数据,另一端用于移除数据;进行插入操作的端称为队尾,进行删除操作的端称为队头。

3、ArrayList、LinkedList、HashSet区别?

ArrayList底层的数据结构使用的是有序数组结构。特点:查询速度很快O(1)。但是增删稍慢O(n); LinkedList底层使用的链表(双向链表)数据结构。特点:增删速度很快O(1),查询稍慢O(n); HashSet元素是无序(存入和取出的顺序不一定一致),因为底层是哈希表结构,元素不可以重复;

image-20201015210120776.png

4、谈谈HashMap

数据结构、初始容量、负载因子、哈希算法、扩容机制、JDK8对于HashMap的优化、如何保证其线程安全、LinkedHashMap

什么时候用:需要存储键与值有对应关系的数据的时候,且不考虑线程同步的因素的情况下使用比较适合

键值存储,根据键可以较迅速的获取到对应的value值。

5、线程安全的容器(集合、Map)有哪些,稍微说一下如何实现的。

Vector 线程安全的List ,几乎不用;

CopyOnWriteArrayListCopyOnWriteArraySet ( 写时复制 \ Lock),常用于 读多写少的并发环境;

HashTable 线程安全的Map,几乎不用;

ConcurrentHashMap 线程安全的Map,使用较多,效率高(分段锁/CAS)

加synchronized关键字保证同步操作,手动实现并发安全,用于并发几率低场景;

使用Lock保证同步操作,手动实现并发安全,用于并发几率较高场景,相比synchronized的使用,Lock使用更多。

使用Collections 的 synchronizedMap方法,自动实现线程安全,运用方便。

6、用两个栈实现一个队列。

  1. // 自定义 队列 ,当前队列 用 两个栈实现
  2. class Queue1 {
  3. // 先创建两个栈 stack1,Stack2
  4. private Stack<Integer> stack1;
  5. private Stack<Integer> stack2;
  6. Queue1() {
  7. this.stack1 = new Stack<Integer>();
  8. this.stack2 = new Stack<Integer>();
  9. }
  10. /**
  11. * 入队
  12. * @param value
  13. */
  14. public void add(Integer value){
  15. stack1.push(value);
  16. }
  17. /**
  18. * 出队
  19. */
  20. public Integer poll(){
  21. if(!stack2.isEmpty()) {
  22. return stack2.pop();
  23. }
  24. // 执行到这里时 说明stack2 是空的
  25. if(!stack1.isEmpty()) {
  26. this.change();
  27. }
  28. if(stack2.isEmpty())
  29. return null;
  30. return stack2.pop();
  31. }
  32. private void change() {
  33. while (!this.stack1.isEmpty()) {
  34. int value = this.stack1.pop();
  35. this.stack2.push(value);
  36. }
  37. }
  38. }

面向对象

1、面向对象编程特性及优势

封装、继承、多态;

代码复用,易于拓展维护,方便实现解耦;

2、重载、重写有什么区别?

重写是子类复写父类方法,对父类方法实现重构;

重载是方法名相同,方法参数个数或方法参数类型不同,重载与返回类型无关。 方法重载可以提高可读性 ;

3、final关键字有什么作用?String类可以被继承吗?

String类被final修饰无法被继承。方法默认指定为final修饰的

final 修饰类,该类无法被继承,不会拥有子类,代表当前类是终类;

final 修饰方法,该方法不能被重写

4、单例模式调用方法跟直接使用类调用静态方法有什么区别?单例存在的意义?

一、静态方法和单例模式的区别 1、静态方法性能更好,在编译期就已经绑定好了。 2、单例模式可以延迟初始化,静态方法在第一次使用时初始化。如果需要加载比较重的对象,用单例模式会更好 3、单例模式可以被继承,方法可以被重写,静态方法不能被重写 4、单例模式适合存状态信息需要改变的需求 二、静态类代替单例模式 如果单例模式不需要维护任何状态,仅仅提供全局方法,这种情况可以用静态类。静态方法比单例要快,静态方法在编译时就已经绑定了。如果需要将一些工具方法集中在一起时,可以采用静态方法,比如collections类提供了一些静态方法用于对集合类进行检索、排序等操作。

5、Object类中的equals方法是如何实现的?==跟equals的区别?为什么要有equals方法?

Object中的equals实则就是用的==;equals存在的意义是为了让子类(引用类型)进行重写覆盖,只有当两个引用代表同一个对象时,==才能为true;

6、反射常用的场景,反射可以用来做什么;

加载类、实例化、获取Method,invoke执行方法,获取Feild,调用Feild get、set;

7、StringBuilder、StringBuffer使用场景,为什么要用它?

字符串拼接;StringBuffer线程安全,StringBuider效率高;避免字符串+号拼接,产生大量新对象,浪费内存空间;

8、OOM、StackOverflow什么情况下出现?有什么区别?

OOM-堆内存空间不足;

StackOverFlow-栈内存不足;

Xmx Xms Xmn Xss

9、设计模式了解么?具体有哪些优势呢?

单例、代理、工厂、策略、观察者 - spring常用

多线程

1、多线程的创建(使用)方式;(共4种)

  • 继承Thread;
  • 实现runnable;
  • 实现callable;
  • 线程池;

2、线程的几种状态,以及它们之间是如何扭转的?

初始化 - > 可执行状态(就绪、运行) -> 阻塞(三种不同的方式)-> 销毁

3、谈谈线程池;

  • Executors;
  • ThreadPoolExecutor
  • 参数意义;
  • 拒绝策略;
  • 哪些地方用到了 — 批量任务处理时、Spring 异步@Async 可以配置指定线程池、mq消费者 配置消费容器工厂多线程消费、调用第三方接口获取用户数据(1000个);

4、说一下volatile跟synchronized的区别,一般用在哪里?

int a= 1;

int b=2;

volatile: 某个线程中需要用到另外一个线程的改变数据;不能保证原子性; 保证可见性、有序性

synchronized: 保证线程安全,并发修改某个资源时,需要考虑 同步 保证安全; 保证可见性、有序性、原子性

5、讲一下synchronized锁升级过程;

没有线程访问(无锁态) —> (无竞争 一个线程访问 )偏向锁 —> (少量竞争(cas自旋))轻量级锁 (自适应自旋锁 轻量级锁) —> 重量级锁(向操作系统申请锁)

6、Lock跟synchronized的区别,一般怎么选择?

Doug Lea

Lock 底层 是 使用 AQS 及CAS 来实现的,核心 compare and swap ,性能要高。自旋比较耗费cpu;

synchronized 1.6之前 就是一个 重量级锁, jvm程序(用户态) 去向 系统 (内核态) 申请 一把锁,会造成线程状态来回切换;

使用层面的区别:

synchronized 隐式 加解锁;

Lock 是显式 的加解锁;Lock 使用 会更加灵活;

7、介绍几个juc工具包常用的并发编程类。

countdownLatch (闭锁) — 多线程 中 控制 任务数据量,可以以此 来判断 某个线程池中的任务是否都已经执行完毕;

Atomic — 原子性 技术 ,可以实现 类似 synchronizad 加锁 保证 i++ 线程安全的操作;

Callable — Future — 可以有返回 值的 一个线程任务 ,可以结合 Future一起使用;

ConcurrentHashMap — 效率较高的 一个 线程安全的 map 容器;

Semphone — 信号量 ,限流; 某个 业务逻辑 最多 允许 同时 让 几个线程 并发执行;

Lock — 可以实现 类似 synchronizad 保证线程安全的功能;

web基础

1、 请问你了解HTTP协议吗?为什么说HTTP是无状态的?如何让HTTP请求“有状态”?

http 协议 请求: 请求头请求行 请求体,响应对应的也会有三部分内容; 请求方式 有 get、post、 delete、put 请求,基于tcp;

服务端 对于 客户的请求 是不做 存储,每次请求响应是单独的。

可以用 cookie session机制 让服务端存储 部分 客户端 认证信息,这样 客户端下次 携带 cookie 访问 后台,后台就能基于 cookie 找到 session,服务端就能知晓这次请求 的客户端信息;

2、cookie、session的区别,用来做什么的?

cookie 服务端产生 返回给 客户端,保存在客户端;

session 服务端生成,保存在 服务端;用于存储 用户的一些信息;

3、Get请求跟Post请求的区别?

get请求 url地址栏传参,内容是明文显示的,并且有大小限制;多用于查询

post请求 多见 在 请求体传参,内容不能直接可见,大小无限制,相对 get请求 更加安全,多用于表单提交;保存、更新 数据;

4、浏览器地址栏输入网址,回车后,发生了哪些事情?

https://www.cnblogs.com/tangjian07/p/10835687.html、https://blog.csdn.net/jiao_0509/article/details/82491299、[https://blog.csdn.net/kuangsonghan/article/details/80046492](https://blog.csdn.net/kuangsonghan/article/details/80046492)

tcp三次握手:

第一次握手

客户主动(active open)去connect服务器,并且发送SYN 假设序列号为J,
服务器是被动打开(passive open)

第二次握手

服务器在收到SYN后,它会发送一个SYN以及一个ACK(应答)给客户,
ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K

第三次握手

客户在收到新SYN K, ACK J+1 后,也回应ACK K+1 以表示收到了,
然后两边就可以开始数据发送数据了

image-20201108201226245.png

https://blog.csdn.net/jun2016425/article/details/81506353

5、说一下Servlet生命周期。

1)初始化阶段 当客户端向 Servlet 容器发出 HTTP 请求要求访问 Servlet 时,Servlet 容器首先会解析请求,检查内存中是否已经有了该 Servlet 对象,如果有,则直接使用该 Servlet 对象,如果没有,则创建 Servlet 实例对象,然后通过调用 init() 方法实现 Servlet 的初始化工作。需要注意的是,在 Servlet 的整个生命周期内,它的 init() 方法只能被调用一次。 2)运行阶段 这是 Servlet 生命周期中最重要的阶段,在这个阶段中,Servlet 容器会为这个请求创建代表 HTTP 请求的 ServletRequest 对象和代表 HTTP 响应的 ServletResponse 对象,然后将它们作为参数传递给 Servlet 的 service() 方法。 service() 方法从 ServletRequest 对象中获得客户请求信息并处理该请求,通过 ServletResponse 对象生成响应结果。 在 Servlet 的整个生命周期内,对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,并且创建新的 ServletRequest 和 ServletResponse 对象,也就是说,service() 方法在 Servlet 的整个生命周期中会被调用多次。 3)销毁阶段 当服务器关闭或 Web 应用被移除出容器时,Servlet 随着 Web 应用的关闭而销毁。在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法,以便让 Servlet 对象释放它所占用的资源。在 Servlet 的整个生命周期中,destroy() 方法也只能被调用一次。 需要注意的是,Servlet 对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭或 Web 应用被移除出容器时,Servlet 对象才会销毁。

image-20201108200300793.png

6、说一下HTTP常见的响应码及分别代表什么含义?

404 资源找不到

400 请求参数数据格式不合法

500 服务器内部错误

302 重定向

200 请求成功

401 未认证

403 没有权限

7、 讲一下五层网络模型。

https://www.cnblogs.com/qishui/p/5428938.html
image-20201108201537895.png

框架

1、说一下spring 体系的常用注解及具体作用。

2、说一下你对Spring IOC的理解。

3、讲一下Spring MVC流程。

4、Spring AOP是什么?有什么作用?如何实现的呢?

面向切面编程。在不改动原逻辑的情况下,实现对目标逻辑增强操作;

动态代理 — JDK动态代理(基于接口)、Cglib动态代理(继续继承);

5、有用过Spring异步处理吗?@Async

搜索 保存 用户 搜索历史记录;

6、讲一下SpringBoot自动配置原理及自定义starter流程。

@SpringbootApplication -> @EnableAutoConfigration -> Selector -> selectImports 方法 -> 读取各个springboot 自动配置 配置文件 -> 加载各个组件的 自动配置类 -> 判断是否导入具体的启动器 来实现 判断是否加载当前自动配置类 -> 创建组件需要 的各个Bean;

7、MyBatis缓存有用过么?

8、MyBatis实现原理有了解过么?

MySQL

1、说一下联合索引,按左匹配原则;

MySql索引:是帮助MySQL高效获取数据的一种数据结构

索引按结构分类:

  • Hash索引:MySQL中Memory存储引擎默认支持的索引类型。
  • B+Tree:MySQL使用最频繁的一个索引数据结构,是InnoDB和MyISAM存储引擎默认的索引类型。特点:所有叶子节点之间都有连接指针,提高查询速度;所有数据存储在叶子节点,减少磁盘的IO次数;非叶子节点只存储key值,树型结构较小

联合索引(a,b,c) 比如: 商品表-联合索引(商家id,商品名称)

要想 在 搜索条件中 使用 商品名称 索引生效,那么 商家id 必须出现在 条件中(必传字段),联合索引 想要右边索引字段生效,必须 左边字段出现在条件中;

以a,b,c 联合索引为例,想要b生效 a字段必须存在条件中,想要c生效,a,b必须存在条件中;

2、索引应用建立在哪些字段上?

1、经常用于查询的条件字段 比如用户手机号码;

2、离散度大(重复值少),像性别这种字段建立索引意义不大;

3、用于表关联的字段上,比如订单表 中的 用户id;

4、用于分组、排序的 字段;

索引是不是越多越好? 答:不是,因为创建索引和维护索引耗时间,还有就是索引需要占物理空间。

3、如何排查慢查询?Explain执行计划主要看什么?现在有一个查询已经影响到数据库的性能了,如何排查跟终止?

:未命中索引的sql查询语句是不允许上线的。

https://www.cnblogs.com/tufujie/p/9413852.html

explain + 具体SQL 查看 SQL执行的执行计划;

  1. // 1.查看最近所有sql进程
  2. show full processlist;
  3. // 2.kill 会话号;
  4. kill 123456;

https://blog.csdn.net/weixin_34055910/article/details/94681488

4、了解MySQL存储引擎么?

MySQL5.7支持的存储引擎包括哦:InnoDB、MyISAM、MEMORY等,其中较为常用的也是这三种

这三种存储引擎的特点分别是:

  • MyISAM:访问快,不支持事务和外键,支持表级锁。表结构保存在.frm文件中,表数据保存在.MYD文件中,索引保存在.MYI文件中,在读数据操作比较频繁的情况下使用它比较好。
  • InnoDB:支持事务,占用磁盘空间大,支持并发控制,支持行级锁。表结构保存在.frm文件中,如果是共享表空间,数据和索引保存在innodb_data_home_dir和innodb_data_file_path定义的表空间中,可以是多个文件。如果是多表空间存储,每个表的数据和索引单独保存在.ibd中。
  • MEMORY:内存存储,速度快,不安全。适合小量快速访问的数据。表结构保存在.frm中

5、数据库优化有哪些手段?具体谈谈。SQL优化有哪些方式?

添加索引、使用缓存、主从同步读写分离、分库分表、选择合适的存储引擎、迁移搜索功能到ES、根据机器性能合理配置数据库参数;

SQL优化:

索引字段避免使用函数,使用函数会导致索引失效;

避免不同类型的字段进行条件比较,否则可能导致索引失效;

like 避免 关键字左边 用 %;

分页查询 翻页 时 可以携带 上一页最后一个id进行条件判断,提升搜索性能;

在进行关联查询时,提前缩小范围再进行关联,可以提升关联效率;

在大量数据转移(插入到新表)时,将数据先转移到一个没有索引(除了主键)的表上,再建立其它索引,这样比,直接转移到一个已经创建好索引的表上,前者效率要高很多;

数据插入采用批量操作;如 INSERT INTO t (id, name) VALUES(1,’Bea’), (2,’Belle’),(3,’Bernice’);不要一条一条插入;

建议数据库隔离级别 使用RC;

6、说一下ACID及MySQL的事务隔离级别。

原子性、持久性、隔离性、一致性

隔离级别 读未提交 读已提交 可重复读 串行化

7、MySQL并发访问是如何保证线程安全的?

MySQL 各个锁

Redis

1、Redis是什么?有哪些优缺点?

  1. redis是由c编写的,是一个Key-Value类型的NoSql,整个数据库系统统统加载内存中,并在内存中进行操作,定期通过异步操作将数据库中的数据刷新到硬盘中进行保存。
  2. 因为是纯内存操作,所以读写数据的效率是相当快的,而且支持多种数据结构,支持高并发等
  3. 主要缺点是redis的数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此redis适合的场景主要局限在较小数据量的高性能操作和运算上

    2、Redis常用数据结构有哪些,结合项目说一下使用场景。

  4. String:普通的 key-value数据类型

  5. hash:值类型为map结构的,多用于存储对象类型的数据,在项目中可以设置一个expire时间,因此可以用作一个功能加强版的memcached来用。
  6. list:有序列表 ,可以通过list存储一些重复元素的列表型数据。在项目中用list来做双向队列型轻量级的消息队列服务。
  7. set:无序集合,自动去重 ,需要对一些数据 快速全局去重的情况可以考虑使用它,基于set玩的是差集、并集和交集完成的
  8. sorted set(zset):排序去重的set,写进去是 给个分数,自动根据分数排序:比如排行榜。

    3、Redis在项目中哪些地方用到了?结合项目说一下。

  9. 用户在查询热数据时会利用redis进行将数据缓存,以减轻数据库的压力,提高查询效率,新闻热点排行榜 等这些动态比较大的数据会把它存在redis中等等。。

4、Redis是单线程执行指令的,为什么那么快呢?Redis是如何保证性能的?

  1. redis是基于内存和宽带的,内存的读写速度是非常快的
  2. redis是单线程的,省去了很多的上下文切换线程的时间。
  3. redis使用了多路复用技术,可以处理并发的连接,非阻塞IO内部实现采用epoll,采用了epoll加上自己实现的简单的事件框架,epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用性,绝不再io上浪费时间。

https://zhuanlan.zhihu.com/p/58038188

5、Redis常见的问题有哪些?如何避免跟解决?

  1. 雪崩:大量缓存同时过期或者Redis宕机,大量并发访问数据库,导致数据库宕机;
    1. 解决方案: 事前:redis 高可用,主从+哨兵,搭建集群,避免全盘崩溃。 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免数据库宕机。 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
  2. 穿透:恶意攻击(id为负数),缓存查不到,每次去数据库查,也查不到;这种恶意的高并发请求就会把数据库打死;
    1. 解决方案:只要数据库没有查到就返回一个空值,存入缓存,并设置过期时间,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
  3. 击穿:某个 key 非常热点,访问非常频繁,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
    1. 解决方案:可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。

      6、介绍一下Redis持久化及Redis集群方式。

7、Redis集群拓展跟迁移方案有了解过吗?

https://www.cnblogs.com/ymaa/p/13420334.html

8、如何保证缓存与数据库的双写一致性?

  1. 先删除缓存,再更新数据库,再写入缓存;
  2. 为什么不是直接修改缓存而是删除?
    1. 多次修改缓存,会导致没有或者很少次数来访问这个缓存;删除缓存后,当再次来查询缓存的时候只需要从数据库中查询一次即可;
  3. 为什么说先删除缓存,而不是先更新数据库?
    1. 先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致;先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。
  4. 如果在删除缓存后,高并发读写情况下,数据库的数据还未修改,已经被读到了并写入缓存,那缓存数据就和数据库不一致了,怎么处理?
    1. 并发量在几千上万的情况下才会出现这种问题;

分布式、微服务

1、介绍一下SpringCloudDubbo的区别?

2、分布式事务如何处理呢?

seata

消息队列 最终一致性。 基于重试,保证消费者幂等;

3、分布式锁有用过么?

4、Spring Cloud组件介绍一下。

微服务间调用 - 申明式调用 feign

网关 - gateway

负载均衡 ribbon

注册中心 eureka 、nacos(spring cloud alibaba)

服务保护 - hystrix

5、Spring Cloud Alibaba有了解过么?

面试必备问题汇总 - 图5

6、你是如何理解分布式的?

7、谈谈微服务的优劣。单体架构一定就差么?如何做技术选型?

消息队列

1、如何保证消息不丢失?分别从生产端、消费端介绍一下。消息的可靠性投递;

生产端

kafka

生产者设置 acks=all,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

rocketmq

我们可以采取同步发送的方式进行发送消息,发消息的时候会同步阻塞等待broker返回的结果,如果没成功,则不会收到SendResult,这种是最可靠的。其次是异步发送,再回调方法里可以得知是否发送成功。

kafka + rocketmq 都可以用

消息落库+定时任务;

消息落库+自发自监;

消费端

kafka

消费者手动ack,确保消息不会丢失;

rocketmq

https://blog.csdn.net/qq_45401061/article/details/108554625

RocketMQ本身提供了重新消费消息的能力 (消费重试);

2、如何实现消费端的幂等性?

加版本号;

update set a = * , version = version +1 where version < #{version}

存储消费记录,已经处理过的业务,则不再处理,数据库主键去重; — 加锁

3、rocketmq 发送消息的几种方式;

https://blog.csdn.net/qq_28851503/article/details/100182071

https://blog.csdn.net/qq_45401061/article/details/108554625

同步发送:Producer 向 broker 发送消息,阻塞当前线程等待 broker 响应 发送结果。

异步发送:Producer 首先构建一个向 broker 发送消息的任务,把该任务提交给线程池,等执行完该任务时,回调用户自定义的回调函数,执行处理结果。

Oneway发送:Oneway 方式只负责发送请求,不等待应答,Producer只负责把请求发出去,而不处理响应结果。

我们在调用producer.send方法时,不指定回调方法,则默认采用同步发送消息的方式,这也是丢失几率最小的一种发送方式。

4、如何实现延迟消费呢?

定时任务(时效性问题)

RabbitMQ 死信队列;

https://www.cnblogs.com/heihaozi/p/13259125.html

rocket mq 延迟消费 在发送消息时,设置消息延迟级别即可 ; 消息延迟级别分别为1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

5、在某种特殊的消费场景中,如何实现消息消费的顺序性?

kafka 分区设置1个分区;1个消费者,单线程消费;

消费者获取到消息后, 存储到 优先级(按发送时间) 队列中,在从队列中 获取消息 顺序消费;

可以用数据库存储消息,未到达消费要求的 消息,暂存 数据库。等前提条件满足后,触发消费(或定时触发消费);

JVM

1、JVM内存区域有了解吗?

运行时数据区

线程隔离的数据区(每个线程都独有一份)
  • 程序计数器> 是记录当前线程所执行的字节码行号指示器 (每个线程都会存在一个这样的小区域(内存空间))
  • Java虚拟机栈> 虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

    如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverFlowError异常(避免深度过深的目的也是为了避免栈空间被耗尽)。 如果线程在申请栈空间时申请失败,没有足够的内存可用(比如线程创建过多),那么会出现OutOfMemoryError(OOM)异常。 如果Java虚拟机栈容量不足,当栈动态扩容时申请不到足够的内存,也会抛出OOM异常;(HotSpot虚拟机的栈容量是不可以动态扩展的)

  • 本地方法栈> 与虚拟机栈所发挥的作用非常相似,区别:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的本地Nativie方法服务;

所有线程共享的数据区
  • Java堆> 虚拟机所管理的内存中最大的一块。这一块内存区域被所有线程共享。

    存放实例对象。 几乎(逃逸技术优化手段的产生,可能又是对象在栈上分配)所有的对象都存储在堆中。 如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机会抛出OutOfMemoryError异常。

  • 方法区> 非堆,各个线程共享区域。

    存储:已被虚拟机加载的的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。 1.8——元空间(本地内存 native memory)

2、垃圾回收算法有哪些?比较一下他们的区别。

对象已死?

引用计数算法
  1. public static User staticU= new User();
  2. f(){
  3. User u1 = new User();
  4. User u2 = new User();
  5. u1.peiou = u2;
  6. u2.peiou = u1;
  7. u1 = null;
  8. u2 = null;
  9. System.gc();
  10. }

根可达分析算法

GC - 垃圾收集 garbage collection

哪些内存需要回收?

什么时候回收?

如何回收?

“自动化”

垃圾回收算法

复制

耗费空间,效率相对较高。

标记清除

效率较高,内存耗费较少,会产生内存碎片

标记压缩

效率较低,内存耗费较少,不会存在内存碎片

2、介绍一下垃圾回收机制,根可达性算法有了解过么?如何判断一个对象是否是垃圾呢?

引用计数算法、根可达分析算法

垃圾回收机制 较复杂 - 自行研究

3、介绍一下垃圾回收器,比较一下他们的优劣,如何选型?

垃圾回收器 较复杂 - 自行研究

4、JVM调优一般调什么?怎么调?

调 内存大小、调GC参数;

jstat -gc jmap jconsele

5、说一下Java的类加载机制。

7、Java内存模型有了解么?

JMM内存模型