文章目录
面试题
java
jvm、gc
-
聊聊你对jvm的认识
JVM由JVM运行时数据区、执行引擎、本地库接口、本地方法库组成。
JVM运行时数据区,分为线程共享部分(方法区、堆)和线程隔离区(虚拟机栈、本地方法栈和程序计数器)。
1.1. 方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池(Runtime Constant Pool)是方法区的一部分。.Class文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将类在加载后进入方法区的运行时常量池中存放.
运行时常量区的内容并不只是在编译期间产生,通过String.intern()也可以实现在运行时向常量区中添加内容。
需要注意的是:从JDK8开始,方法区被元数据区替代了。具体的原因和两者的区别可以参考官网。
2.堆
是JVM中最大的一块内存区域,该区域的目的只是用于存储对象实例及数组。该区域也是GC的最主要区域。
根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样.在实现时,既可以实现固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制).如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,就会抛出OutOfMemoryError异常。
3.虚拟机栈
每个线程方法在执行时都会创建一个栈帧,包含局部变量表、返回地址、操作数栈等信息。每个方法的执行与完成就对应的栈帧的入栈与出栈过程 。局部变量表占用空间的大小在编译期就确定了。这里需要注意:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时将会抛出OutOfMemoryError异常。
4.本地方法栈
与虚拟机栈类似,不过其中执行是本地方法。对于HotSpot虚拟机而言,本地方法栈和虚拟机栈是统一的。
5.程序计数器
是一个小的内存空间,如果线程正在执行的是一个java方法,则此内存区域记录正在执行的虚拟机字节码指令的地址;如果线程正在执行的是native方法,则计算器中的值为空。此内存区域是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemoryError情况的区域。这几部分都有相关的JDK自带工具可以分析查看,比如jps, jstack, jmap, jhat, jstat等,还有图形化工具jconsole,jvisualvm,但对于Linux服务器就无能为力了。
好了,关于JVM就讲完了,接下来进行提问:JVM哪些区域会发生OOME,以及各自原因是什么?
堆内存不足是最常见的 OOM 原因之一,抛出的错误信息是“java.lang.OutOfMemoryError:Java heap space”,原因可能千奇百怪,例如,可能存在内存泄漏问题;也很有可能就是堆的大小不合理,比如我们要处理比较可观的数据量,但是没有显式指定 JVM 堆大小或者指定数值偏小;或者出现 JVM 处理引用不及时,导致堆积起来,内存无法释放等。
而对于 Java 虚拟机栈和本地方法栈,这里要稍微复杂一点。如果我们写一段程序不断的进行递归调用,而且没有退出条件,就会导致不断地进行压栈。类似这种情况,JVM 实际会抛出 StackOverFlowError;当然,如果 JVM 试图去扩展栈空间的的时候失败,则会抛出 OutOfMemoryError。
对于老版本的 Oracle JDK,因为永久代的大小是有限的,并且 JVM 对永久代垃圾回收(如,常量池回收、卸载不再需要的类型)非常不积极,所以当我们不断添加新类型的时候,永久代出现 OutOfMemoryError 也非常多见,尤其是在运行时存在大量动态类型生成的场合;类似 Intern 字符串缓存占用太多空间,也会导致 OOM 问题。对应的异常信息,会标记出来和永久代相关:“java.lang.OutOfMemoryError: PermGen space”。
随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的 OOM 有所改观,出现 OOM,异常信息则变成了:“java.lang.OutOfMemoryError: Metaspace”。
直接内存不足,也会导致 OOM。 -
对GC了解哪些
参考链接:谈谈对Java GC的了解
jdk1.8的新特性
- jdk1.8引入了哪些新特性,你用过哪些
参考链接:JDK1.8 新特性(全)
java基础
- 你对Collection的了解
参考链接:java集合Collection常用方法详解 - HashMap了解哪些
参考链接:java中hashmap的实现原理与底层数据结构 - 循环list/map有哪几种方式,优缺点是什么
参考链接:Java 遍历List或Map集合的4种方式 - 实现事务有哪几种方式
参考链接:Spring事务管理之几种方式实现事务 - @Transactional是如何实现事务的
参考链接:@transactional注解原理 - 设计模式有哪些,在项目中用了哪些
参考链接:项目中用到的设计模式 - 事务不生效的场景有哪些
参考链接:@Transactional来控制事务,事务不生效的场景
框架
spring
- ioc
参考链接:谈谈对Spring IOC的理解 - aop
参考链接:Spring AOP——Spring 中面向切面编程
spring boot
- 为什么使用springboot
参考链接:为什么要使用Springboot - 自动装配是如何实现的
参考链接:SpringBoot自动装配原理分析
mybatis/mybatis-plus/springDataJpa
- mybatis中#和$的区别
参考链接:https://www.cnblogs.com/hellokitty1/p/6007801.html - 一对多如何实现
参考链接:mybatis的一对多,多对一,以及多对对的配置和使用 - mybatis-plus与mybatis的区别
参考链接:Mybatis-Plus和Mybatis的区别 - mybatis与springDataJpa的区别
参考链接:深入浅析Mybatis与Hibernate的区别与用途 - mybatis-plus的批处理
参考链接:给你的MyBatis-Plus装上批量插入的翅膀
面试官问这个问题是为了看是否看过mybatis-plus的批处理源码,了解过的话它其实就是一个for循环单条插入。。。。。。 - 二级缓存
参考链接:Spring二级缓存
参考链接:MyBatis 二级缓存全详解
mysql
sql优化
- 发现一个sql查询过慢,如何去优化它
参考链接:一条sql执行很慢,可能是因为什么? 怎么优化? - 如何查看这个sql执行计划
参考链接:查看sql语句的执行计划 - 是否有分库分表或读写分离
参考链接:分库分表、主从、读写分离
索引
- mysql索引都有哪些,平时使用哪些
参考链接:mysql 有哪些索引 - InnoDB与MyISAM的区别
参考链接:mysql中innodb和myisam对比及索引原理区别 - 索引的数据结构,为什么选择b+树
参考链接:MySQL索引底层结构为什么选择B+树
事务
-
事务的传播机制
类型 说明 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 -
事务的四大特性
- 原子性:事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。
- 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。
- 隔离性:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。
- 持久性:指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其执行结果有任何影响。
-
事务的隔离级别(该问题存在于事务的并发问题,mysql默认的事务隔离级别为repeatable-read)
事务隔离级别 脏读 不可重复读 幻读 读未提交(read-uncommitted) 是 是 是 不可重复读(read-committed) 否 是 是 可重复读(repeatable-read) 否 否 是 串行化(serializable) 否 否 否 - 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
-
乐观锁与悲观锁,如何实现乐观锁
4.1 概念:- 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
- 悲观锁,从字面理解就是很悲观,每次去拿数据的时候都认为别人会修改,所以在每次拿的时候对数据上锁,这样就保证了数据的准确性。比如mysql中的表锁,行锁。
- 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
- 乐观锁,在每次去拿数据的时候认为别人不会修改,不对数据上锁,但是在提交更新的时候会判断在此期间数据是否被更改,如果被更改则提交失败。
4.2 实现:
- 使用版本控制字段,再利用行锁的特性实现乐观锁,如下
有一张订单表order,有字段id、order_no、 price, 为实现乐观锁控制,添加version字段,默认值为0
id | 1 |
order_no | 123456 |
price | 5 |
version | 0 |
1. 先查询该数据 select * from order where id = 1
2. 修改该条数据 update order set price = 1 where id = 1
如果两个人同时查询到该条数据price = 5,可以执行update操作,但任意一方还没执行update操作,那么最后双方都执行update,导致数据被修改两次,产生脏数据!
* 使用version字段控制版本后:
1. 两人先查询该数据 select * from order where id = 1
此时两人查询到的数据一样,id = 1, price = 5, order_no = 123456, version = 0
2. 两人都发现该条数据price = 5, 符合update条件,第一人执行update(因为mysql行锁的特性,两人不可能同时修改一条数据,所以update同一条数据的时候,是有先后顺序的,只有在第一个执行完update,才能释放行锁,第二个继续进行update):
update order set price = 1, version = version + 1 where id = 1 and version = 0
3. 执行完成后,version字段值将变成1, 第二人执行update:
update order set price = 1, version = version + 1 where id = 1 and version = 0
**此时的version的值已经被修改为1,所以第二人修改失败,实现乐观锁控制。**
redis
应用场景
-
redis主要用来做什么
众多语言都支持Redis,因为Redis交换数据快,在服务器中常用来存储一些需要频繁调取的数据,节省内存开销,也极大的提升了速度。
将一些热点数据存储到Redis中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。- 会话缓存(最常用)
- 消息队列(支付)
- 活动排行榜或计数
- 发布,订阅消息(消息通知)
- 商品列表,评论列表
-
在你项目中哪里用到了redis,用来做了什么(主要讲讲在自己项目中怎么用的就行,会往深了的业务问)
参考链接:redis常见应用场景
多线程
- 实现多线程有哪几种方式
参考链接:java多线程的6种实现方式详解 - 在你项目中哪里用到了多线程
参考链接:项目中哪里用到了多线程? - 线程池创建的方式及参数
参考链接:创建线程的四大方法及线程池的七大参数
服务器相关
-
linux下载安装jdk需要哪些命令
参考链接:linux下安装jdk 详细步骤 -
docker的挂载,是否做过自动化部署
参考链接:docker基础知识之挂载本地目录
参考链接:Docker自动化部署(超详细) -
nginx的应用场景
-
nginx的正向、反向代理
- 正向代理:服务器位于客户端和服务器之间,为了从服务器获取数据,客户端要向代理服务器发送一个请求,并指定目标服务器,代理服务器将目标服务器返回的数据转交给客户端。这里客户端需要要进行一些正向代理的设置的。
正向代理中被代理的是客户端的请求(比如翻墙) - 反向代理:客户端对代理是无感知的,客户端不需要任何配置就可以访问,客户端将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。
- 正向代理:服务器位于客户端和服务器之间,为了从服务器获取数据,客户端要向代理服务器发送一个请求,并指定目标服务器,代理服务器将目标服务器返回的数据转交给客户端。这里客户端需要要进行一些正向代理的设置的。
算法
- 排序算法除了冒泡排序外还了解哪些
参考链接:JAVA排序算法:2、选择排序
参考链接:JAVA排序算法:3、插入排序
参考链接:JAVA排序算法:4、快速排序
持续更新中… - 递归求n的阶乘(该题是从笔试题上看到的)
public class Factorial { public static void main(String[] args) { System.out.println("n=1 n阶乘为:" + factorialN(1)); System.out.println("n=2 n阶乘为:" + factorialN(2)); System.out.println("n=3 n阶乘为:" + factorialN(3)); System.out.println("n=4 n阶乘为:" + factorialN(4)); } private static int factorialN(int n) { //判断传进来的n是否为0,若为零返回阶乘为1 if (n == 0) { return 1; } return n * factorialN(n - 1); } }
- 二分查找算法
参考链接:二分查找算法
TCP/HTTP/WebSocket
- TCP为什么会出现粘包现象,有何解决方案
- 产生粘包问题的两个原因:
1、发送方产生粘包:TCP协议通信连续多次调用send向对方发送数据,如果两次调用send的时间间隔极短,TCP协议为提高传输效率会将两次发送数据打包为一个数据包发送(Nagle算法)
2、接收方产生粘包:如果在接收方接收数据时,接收方没有及时调用recv接收数据而下一个数据包又发送过来则recv在接收数据时会将两个或多个数据当做一个数据包处理,同样会产生数据粘包现象 - 解决方法:
1、取消TCP协议的优化机制(不是很好,降低传输效率)调用Setsockopt函数
2、延长连续两次发送数据的时间间隔(比如调动Sleep函数)
3、加包头(在发送数据之前先发送数据的大小,类型等相关信息)或者加间隔(在每段数据的结尾添加标识符以区分不同的数据段),一般包头数据都是定长的(项目中最完美的解决方案也是最常用的解决方案)
接收方粘包问题的解决方案:对于接收只能尽可能的及时接收数据以避免粘包
- 产生粘包问题的两个原因:
- HTTP请求与WebSocket区别
参考链接:webService、webSocket、socket、http之间的区别 - webSocket连接断开怎么办
参考链接:WebSocket断开原因分析,再也不怕为什么又断开了
业务
- 单点登录如何实现(不适用安全框架)
主要考察实现SSO的思路 - 让你设计一个接口给第三方调用,你会如何设计
需要从接口设计(对参数的检验、格式转换)、并发(是否有高并发情况需要负载均衡)、安全机制(有效期token、IP白名单、数据加密)、拦截机制(重复请求的拦截)等方面去考虑 - 写一个接口需要用到哪些注解
主要考察业务的深度,多讲些能提升你业务水准的注解例如:- controller层:
@RestController //相当于@ResponseBody + @Controller @RequestMapping //指定url,映射请求 @GetMapping //仅接收Get请求 @PostMapping //仅接收Post请求 @PutMapping //仅接收Put请求,Restful风格 @DeleteMapping //仅接收Delete请求,Restful风格 @RepeatSubmit //防止重复提交 @RequiresPermissions//限制接口权限 @Api //Swagger注解,使用在类上,表明是swagger资源 @ApiOperation //使用于在方法上,表示一个http请求的操作
- service层:
@Service //标注服务层 @Slf4j //logback日志 @Autowired //自动注入 @Component //把普通pojo实例化到spring容器中 @DS //多数据源切换 @Transactional //开启事务
文章部分内容参考链接:
https://www.cnblogs.com/jpcflyer/p/10666099.html
https://blog.csdn.net/lp2388163/article/details/80683383
https://www.cnblogs.com/cye9971-/p/11267431.html
https://blog.csdn.net/you_ranxi/article/details/103048747
http://www.voidcn.com/article/p-srcudkft-gq.html
感谢各位原创作者,“我们不生产水,我们只是大自然的搬运工”。
总的来说三年经验面试官问的基础问题变少了,开始问框架底层实现,从事java开发还是要做深做广,该文仅记录本人面试经验,若有错误之处还请指出,谢谢。