面试汇总之百分之一
1.关于redis的缓存击穿和雪崩
缓存击穿:redis缓存系统是根据key来查询value的值,当value不存在的时候,就会去访问数据库(DB),如果大量的请求进来找不到与之对应的value时,会对数据库造成巨大压力,以至于导致数据库瘫痪,这就叫缓存击穿。
解决方案:1.使用布隆过滤器
2.不管查询的value是null还是不存在都将值缓存下来,对于这 样的key设置的过期时间比较短。
缓存雪崩:当缓存服务器重启或者某一时间段缓存大量失效,这样失效的时 候会导致大数据量访问数据库(DB),导致数据库压力太大,导致瘫痪的情况。
解决方案:
1.redis集群 或则DB数据库集群,当某一台服务器挂掉,其他的服务
器顶上。
2.资源保护:可以使用熔断器(netfilx中的hystrix)处理,各种资源线
程的隔离,保护主线程。
3.设置key不过期
4.不同的key设置不同的过期日期,使key在时间分布上比较均匀,防
止大量key同时失效。
5.互斥锁:在缓存失效的时候,通过加锁来控制读写数据库的线程数量,
对于一个key只允许一个线程来查询数据库,其他线程等待。
2.JVM结构原理 和GC工作机制
GC注意点:GC回收的是无任何引用对象的内存空间,垃圾回收机制回收的是无任何引用的对象的内存空间并不是对象本身。
GC的算法:1.引用计算法 2.可达性分析法
GC回收的主要是堆和方法区的内存
JVM内存区结构解析:
JVM主要有五部分组成:方法区 java栈 本地栈 堆 程序计数器
方法区:主要存放类结构信息的变量,静态变量,常量池,构造方法
java栈:java栈和线程关联在一起,每当创建一个线程时,就会给线程分配一个java栈。一个Java栈中又分为多个栈帧,每当执行一个方法就会创建一个栈帧,栈帧的作用是存储局部变量,操作栈,方法返回,每个方法执行调用返回的过程就是栈帧在Java栈入栈和出栈的过程。
本地栈:JVM使用到本地内容的时候
堆:主要存放Java实例或者对象的地方
程序计数器:主要是保存当前线程执行的内存地址,因为程序都是多线程执行的 cup随机切换线程的执行顺序,所以程序计数器的存在可以记录上次线程执行中断的地方,当下次切换到此线程执行时可以直接恢复到中断地方执行即可。
Java内存分配有两种:静态内存和动态内存
- 静态内存:指一开始就知道内存单元大小的,例如int类型
- 动态内存:指随机内存大小的 比如:new 个对象
注意jvm内存分配和C/C++不同,jvm事先分配一大块内存单元,我们在 创建对象是在jvm事先分配的单元中分配存储单元。
垃圾检测和回收算法:
垃圾回收器:会做两个操作
一是垃圾检测方法
方法有两种:
- 引用计数法:给每一个对象添加引用计数器,每多一个引用,引用计数器加一,引用位0的视为垃圾
- 可达性分析法: 以根集点作为起始点进行搜索,如果对象不可达的话,视为垃圾对象
二是垃圾回收方法
- 标记-清除:标记垃圾对象然后清除,最基础的算法,但是效率低,删除后会产生大量的碎片
2.复制:复制算法就是把内存空间分为相同的两块,遍历当前使用的区域,遍历处于正在使用的对象复制到另一个内存空间中。此时复制成本小,不会产生大量碎片。
3.标记-整理:整合个前两个的过程,首先是标记所有在引用的对象,然后将标记的对象压缩到堆的一块空间。此方法同样避免了碎片的产生。
4.分代收集算法:
现在的jvm主要应用分代收集算法
分为(Young)年轻代(Old)年老代(Permanent)持久代
年轻代分为三个部分 一个Eden区 和两个Survivor
一个Java对象从开始到结束的一生
(年轻代)Java出生:Eden—执行minor—>Survivor
多次GC周期的执行筛选出年轻代有资格进入年老代的对象,过程中有年龄阀的限制。
年轻代----执行majorGC---->年老代
年轻代用的算法为复制算法
年老代用的算法为标记—整理
3.java对象的生命周期
1. 创建对象(created)(为对象分配内存空间--开始构造对象—从超类到子类对静态变量赋初始值—超类成员变量按顺序初始化,递归调用超类的构造方法—子类成员变量按顺序初始化,递归调用超类的构造方法)
2.应用阶段
3.不可见阶段:(超出作用域使用)但系统还存在强引用,无法回收
4.不可达阶段
5.收集阶段
4.hashMap的实现原理
数组:占用连续的内存单元,空间复杂度大 时间复杂度为o(1),寻址容易,插入删除比较慢
链表:不占用连续的内存单元,空间复杂度小,时间复杂度大,寻址复杂,增删比较快
HashMap : 数据结构是 链表数组类型,根据hash(key)%len决定在哪个链表数组上。