1、Java内存模型是什么?
JMM即Java内存模型(Java memory model),在JSR133里指出了JMM是用来定义一个一致的、跨平台的内存模型,是缓存一致性协议,用来定义数据读写的规则。Java内存模型规范java如何按需禁用缓存和编译优化的方法。
2、mysql查询第一万条数据后20条数据,sql语句如何写?
select * from demo.order where id> (select id from demo.order order by order_no limit 10000, 1) limit 20;
3、设计模式 GOF 23 ?
创建型:单例模式,抽象工厂模式,工厂方法模式,建造者模式,原型模式
结构型:适配器模式,桥接模式,组合模式,装饰模式,外观模式,享元模式,代理模式
行为型:模板模式,解释器模式,策略模式,状态模式,访问者模式,观察者模式,备忘录模式,中介者模式,迭代器模式,命令模式,责任链模式。
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
4、K8S 有哪些组件 及安装过程?
Master组件:
✱ Kubernetes API Server (kube-apiserver):Kubernetes的“心脏”,是集群控制的入口进程,也是Kubernetes所有资源增、删、查、改等操作的唯一入口。
✱ Kubernetes Controller Manager(kube-controller-manager):Kubernetes资源对象的”大总管”,是所有资源对象的自动化控制中心,比如Deployment中的pod副本数。
✱ Kubernetes Scheduler(kube-scheduler):Kubernetes的”调度室“,负责资源调度(Pod调度)。
etcd:是用于共享配置和服务发现的分布式,一致性的KV存储系统,被用作Kubernetes集群后端数据的持久化存储。
Node组件:
✱ kubelet:负责Pod对应容器的创建、启停和销毁等任务
✱ kube-proxy:与Kubernetes Service通信与负载均衡机制
✱ Container Runtime: Docker Engine,负责本机容器的创建、启停与销毁等工作。
5、java程序的生命周期?
加载 验证 准备 解析 初始化 使用 卸载
6、Istio 是什么?
Istio 提供一种简单的方式来为已部署的服务建立网络,该网络具有负载均衡、服务间认证、监控等功能,只需要对服务的代码进行一点或不需要做任何改动。想要让服务支持 Istio,只需要在您的环境中部署一个特殊的 sidecar 代理,使用 Istio 控制平面功能配置和管理代理,拦截微服务之间的所有网络通信。
Istio主要功能:
1、流量管理:负载均衡、动态路由、灰度发布、故障注入
2、可观察性:调用链、访问日志、监控
3、策略执行:限流、ACL(访问控制列表)
4、服务身份和安全:认证、鉴权
7、java和go 内存逃逸有什么区别?
内存逃逸现象:当一个对象不仅在方法中被调用,还在其他地方被引用了,当这个方法结束时,gc无法就立即回收这个对象。
内存对象逃逸分析指的是在方法体内生成的对象被外部引用。
逃逸分析:
如果在方法体内生成了一个对象,并且没有发生逃逸行为,就会使用标量替换,避免在堆上生成对象,提高效率。
如果分析到一个对象没有发生逃逸行为,则jvm 会针对该对象进行同步消除。
将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
go 内存逃逸: golang中内存分配有栈(stack)和堆(heap)两种; 栈分配:分配速度快,只需要CPU的两个指令“PUSH”和“RELEASE“进行分配和释放。 堆分配:分配速度较慢,首先需要找到一块大小合适的内存块,之后还需要gc垃圾回收才能释放。
golang中常见的内存逃逸场景 1.函数内将局部变量指针返回,被外部引用,其生命周期大于栈,溢出。
如何避免内存逃逸 对于性能要求比较高且访问频次比较高的函数调用,应该尽量避免使用接口类型。 不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销更大。 预先设定好slice长度,避免频繁超出容量,重新分配。
8、1000亿整数文件取最大的一百个?
思路: 取文件中前K个数在内存中维护一个长度为K的小顶堆,然后从文件中挨个读取数字并和堆顶比较,如果比堆顶小则直接丢弃,否则替换堆顶后调整小顶堆。遍历完文件中所有的数字后,小顶堆中的K个数就是所求的Top K。
优点: 只需要遍历一次文件中的数字,不存在多次读写数据的问题。
复杂度:
- 时间复杂度:最好情况下文件中前K个数就是Top K,遍历一遍文件即可,时间复杂度为 O(n) ;最坏情况下遍历文件中每个数都需要调整小顶堆,时间复杂度为 O(nlog2k)
- 空间复杂度:只需要在内存中维护小顶堆,空间复杂度为 O(k)
9、如何尽可能避免死锁?
1、预防死锁:通过设置一些限制条件,去破坏产生死锁的必要条件
2、避免死锁:在资源分配过程中,使用某种方法避免系统进入不安全的状态,从而避免发生死锁
3、检测死锁:允许死锁的发生,但是通过系统的检测之后,采取一些措施,将死锁清除掉
4、解除死锁:该方法与检测死锁配合使用
10、MySQL和Redis如何保证数据一致性?
1、先写 Redis,再写 MySQL
这种方案不好,万一 DB 挂了,把数据写到缓存,DB 无数据,这个是灾难性的;
2、先写 MySQL,再写 Redis
对于并发量、一致性要求不高的项目,很多就是这么用的,但是不建议采用这种方案,当 Redis 瞬间不可用的情况,需要报警出来,然后线下处理。
3、先删除 Redis,再写 MySQL
会引起不一致,不推荐。
4、先删除 Redis,再写 MySQL,再删除 Redis
这种方式虽然可行,但是感觉好复杂,还要搞个消息队列去异步删除 Redis。
5、先写 MySQL,再删除 Redis
比较推荐这种方式,删除 Redis 如果失败,可以再多重试几次,否则报警出来;
这个方案,是实时性中最好的方案,在一些高并发场景中,推荐这种。
6、先写 MySQL,通过 Binlog,异步更新 Redis
对于异地容灾、数据汇总等,建议会用这种方式,比如 binlog + kafka,数据的一致性也可以达到秒级;
纯粹的高并发场景,不建议用这种方案,比如抢购、秒杀等。
以下三种方案都是不可取的方案,面试的时候要注意。
①先更新数据库,再更新缓存
②先删除缓存,再更新数据库
③先更新数据库,再删除缓存
最常用的是三种必然会引起不一致的方案,这三种方案大同小异。面试的时候要记住为什么它们会引起不一致。这三种方案都是有一个显著特征,就是如果缓存是会过期的,那么它们最终都会一致。
更多内容,微信搜一搜:后端开发面试题