后端面试
# 后端面试
项目经验:
类淘宝项目,采用springboot+vue开发,分成前台门户系统和后台管理系统,后台单页面vue前后台,前台采用Thymeleaf生成静态页面,前后台共享微服务,如商品微服务,搜索微服务,Eureka注册中心,Zuul网关等
用到技术:
Elasticsearch 所有商品数据封装好后添加到Elasticsearch的索引库中,然后进行搜索过滤,查询相应的商品信息
RabbitMQ短信微服务,其他微服务通过通过mq通知短信微服务,短信微服务接到通知通过阿里大于短信服务发送信息
Redis购物车缓存,短信服务的验证码
JWT+rsa 加密技术进行权限认证
nginx+Thymeleaf 商品详情模块其进行静态化处理,保存为静态html文件。在用户访问商品详情页面时,让nginx对商品请求进行监听,指向本地静态页面,如果本地没找到,才反向代理到页面详情微服务端口。
面试题
java内存模型
线程独有:vm stack,本地方法栈,程序计数器:指示当前程序的位置
metaspace (运行时常量,)heap:新生代,老年代(字符串常量)
事务:
脏读:一个事务读到了另一个事务还未提交的数据
不可重复读:同一个事务中两次读取到的数据不一致
幻读:同一个事务两次读取到的数据增多或减少
隔离级别
读未提交
读已提交oracle
可重复读mysql
进程和线程:
进程是资源管理的最小单元,而线程是程序执行的最小单元
一个Java源码只能定义一个public类型的class,并且class名称和文件名要完全一致 不写public,也能正确编译,但是这个类将无法从命令行执行。
整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值 NaN表示Not a Number Infinity表示无穷大 -Infinity表示负无穷大 可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值
# final
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override,能被调用 如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final。用final修饰的类不能被继承 对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改,能被调用
# static继承问题
1)子类是不继承父类的static变量和方法的。因为这是属于类本身的。但是子类是可以访问的。 2)子类和父类中同名的static变量和方法都是相互独立的,并不存在任何的重写的关系。
# 接口
在Java中,类的多继承是不合法,但接口允许多继承 A:成员变量 只能是常量。默认修饰符 public static final B:成员方法 只能是抽象方法。默认修饰符 public abstract 实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法
# 反射
一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。 从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。尽管在这样的定义与分类下Java不是动态语言, 它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。 换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods
# io
gdt将内存分成内核态和用户态
cpu切换进程过程:中断(如时间中断)打断,查看idt写的进程调度的地址,进程调度根据调度算法找下一个程序,保护上一个进程的现场进入内存,切换到下一个进程
系统调用进入内核态过程:app去调用write函数,write会变成参数保存在寄存器里(include/unistd),然后调int80(软中断),去查idt找到对应的中断服务程序system_call( 和int80在kernel/sched.h和关联l)跳过去进入内核态。write函数阻塞,保护现场,system_call读出寄存器中的参数,执行内核中sys_write代码,执行完毕后恢复write现场,将结果返回
网卡io中断cpu将数据包搬到内存
bio每一个连接对应一个线程
strace -ff -o out java
追踪每个线程系统调用
man 2 blind listen accept
nio:newio
nonblocking: 一个线程对应所有客户端数据 fcntl对文件描述符设置非阻塞,没有连接accept,read直接返回-1
弊端:疯狂系统调用,耗资源O(n)
多路复用器:select(posix标准),poll,epoll。程序主动去读:同步io模型,异步IO则反过来,是指内核kernel是主动发起IO请求的一方,用户线程是被动接受方
- select 程序调用select,内核判断有没有连接,省去大量的系统调用减少资源消耗,返回所有客户端状态集合,程序还需要自己去拷贝数据O(m)
流程:开辟一个用户态的空间,将server和客户端注册进去,一次系统调用,内核来查看有多少个连接,返回客户端状态
弊端:每次重复传递数据,内核要遍历
poll
- epoll
selector.open=epoll_create
server.register=epoll_ctl
selector.select=epoll_wait
- 虚引用:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。用法:管理堆外内存(操作系统)
- 虚引用和threadlocal:threadlocal原理是将当前线程的threadlocalmap中加入key为threadlocal的对象和value为实例对象的方式,key是一个软引用,基本threadlocal用remove方法就可以了。remove方法的关键就在于主动断开entry的key的引用链接,这样在后续的expungeStaleEntry方法中,就会将这种key为null的entry给设置为null,方便GC对内存进行回收。
- java线程声明周期:new,runnable(ready,running),blocked,time_waiting,terminate
自旋锁:死循环加cas的锁,优点:不需要进入内核态,缺点:拿不到锁会一直死循环,消耗cpu资源
重量级锁:靠内核的mutex互斥量加锁
synchronized解析:
无锁:正常对象
偏向锁:运行4秒后创建的对象,thread域为0,加锁后,thread为加锁的线程
轻量级锁:当其他线程对偏向锁对象加锁,升级为轻量锁。
重量级锁:1.4前重量级锁,1.4后结合自旋锁和mutex,竞争不激烈自旋(preblockspin)10次获得锁,竞争过于激烈一直拿不到锁使用重量级锁,如果还拿不到锁就进入阻塞。当有竞争时,无论什么锁直接升级成重量级锁
对象组成:
markword记录锁信息:64bit
原理介绍:
偏向锁:
轻量锁:先在栈上创建lockrecord,cas(ptr,0,lockrecord)成功,将mk复制到lockrecord的m部分,o部分保存指向锁对象的指针。加锁程控,cas操作失败,有两种情况,第一种是重入,说明ptr指向另一个localrecord,只要找到当前线程的栈中的ptr