Docker和K8S
GIT LINUX MYSQL Maven常用的命令
ps(process status)
ps -aux 查看进行所有信息
ps -aux|grep java/进程号 通过管道符和grep过滤
kill -9 进行号 强制关闭
程序 进程 线程 协程
序列化 反序列化
Redis分布式锁 拦截器
面经看到的面试题
计网参考资料:
计操参考资料
面经补充
计网
四次挥手后,客户端等待2MSL?
Dijkstra
广度优先的思想维护两个数组 zhuanlan.zhihu.com/p/346558578
输入url到页面加载过程
1.保证客户端发送的ACK报文段能够到达服务器,从而保证tcp连接能够进行可靠的关闭。
这个很好理解,如果客户端发动ACK后就立刻关闭,那么如果ACK丢失的话,服务端就会一直处于等待关闭确认的状态,超时后再发送关闭请求时,此时的客户端已经关闭,那么服务端就无法进行正常的关闭。
2.保证此次连接的数据段消失,防止失效的数据段。
客户端在发送ACK后,再等待2MSL时间,可以使本次连接所产生的数据段从网络中消失,从而保证关闭连接后不会有还在网络中滞留的数据段去骚扰服务端;
再有一点就是,如果客户端重新发送请求,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了
SSL/TLS怎么保证可靠?
1 两者的连接:CA对证书私钥加密,客户端解利用CA的公钥解密,判断是否是真实的服务端;
2 数据传输:客户端从CA拿到数字证书后,就能拿到服务端的公钥
客户端生成一个Key作为「对称加密」的秘钥,用服务端的「公钥加密」传给服务端 服务端用自己的「私钥解密」客户端的数据,得到对称加密的秘钥,这样就把对称秘钥传输给了服务端
之后客户端与服务端就可以使用「对称加密的秘钥」愉快地发送和接收消息
SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密,然后再用堆成的秘钥进行数据传输。
补充:SSL/TLS的四次握手,目前网上的主流答案都在重复阮一峰老师的博客,属于TLS 1.0版本的答案,使用RSA密钥交换算法。但是现在TLS 1.2已经成为主流,使用ECDHE算法,如果面试可以说出这个版本的答案,应该会更好。
操作系统
进程上下文切换和线程上下文切换
不同的进程对应着不同的虚拟内存,因此进程切换需要对虚拟地址切换,而虚拟内存是通过页表来映射的,就需要把高速缓存TLB(快表)需要来切换,快表在一种特殊的高速缓存中;此外,还需要把寄存器中的缓存来切换
Java
Java基础
元注解
如何理解String不可变?
CGLIB和JDK代理
final修饰char[] 只是表示引用地址不可变,但是内部的数是可以变的。
所以String是不可变,关键是因为SUN公司的工程师,在后面所有String的方法里 很小心的没有去动Array里的元素,没有暴露内部成员字段。private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁上继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。
接口和抽象类的区别
JVM
对象分配内存时候TLAB是什么?
新生代三个区域的关系
因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
为什么需要两个survivor区域?
防止产生碎片,两个相同大小的survivor可以利用标记-复制算法解决碎片化的问题,可见blog.csdn.net/weixin_4396…
垃圾回收补充
这里的效率问题指的是效率不稳定,标记和清除的执行效率随对象增长而降低
标记-复制算法需要额外的空间担保,以便应对所有对象都存活的情况,当前大多新生代采用这种算法,但是老年代一般不直接用(老年代的回收率要低)
CMS的缺点
具体在于
- 并发 会占用一部分线程 处理器核心数少的话 影响用户程序的使用,提高负载
- 在并发标记和清除阶段,由于用户线程还是继续运行的,还是会有新的垃圾产生,是在标记结束之后的,只能留到下一次垃圾清除才可以处理,这一阶段的垃圾也叫"浮动垃圾"
G1
G1与CMS的不同在于,除了并发标记阶段,其余阶段用户进程是暂停的,不是为了极致的追求效率。最后的筛选回收阶段也是暂停用户进程的。
筛选回收阶段,指定回收计划,把需要回收的Region复制到空的Region区域,再清理旧区域,
具体的特点?待补充
G1不再死板的安装固定大小的数量的分代区域,而是根据时间需要来大概的划分Region。 之所以可预测的停顿,是因为把Region作为回收的最小单元,根据用户设定的回收时间,优先处理回报率最大的Region
如何打破类的双亲委派机制?
Java容器
HashMap在jdk1.7采用头插法,1.7采用了尾插法
区别?blog.csdn.net/qq_29978863… 为什么头插会造成循环?
Java并发
深入理解Java很值得一看,对所得分类和介绍比较清晰
JavaGuide中队双重校验锁进行了介绍,但是为什么需要双重锁,可以参考 双重校验锁实现单例模式
jdk1.6对synchronized进行了哪些优化?
volitile为什么没有体现原子性?synchronized为什么体现了有序性?
一个变量i被volatile修饰,两个线程想对这个变量修改,都对其进行自增操作也就是i++,i++的过程可以分为三步,首先获取i的值,其次对i的值进行加1,最后将得到的新值写会到缓存中。 线程A首先得到了i的初始值100,但是还没来得及修改,就阻塞了,这时线程B开始了,它也得到了i的值,由于i的值未被修改,即使是被volatile修饰,主存的变量还没变化,那么线程B得到的值也是100,之后对其进行加1操作,得到101后,将新值写入到缓存中,再刷入主存中。根据可见性的原则,这个主存的值可以被其他线程可见。 问题来了,线程A已经读取到了i的值为100,也就是说读取的这个原子操作已经结束了,所以这个可见性来的有点晚,线程A阻塞结束后,继续将100这个值加1,得到101,再将值写到缓存,最后刷入主存,所以即便是volatile具有可见性,也不能保证对它修饰的变量具有原子性。
加了锁之后,只能有一个线程获得到了锁,获得不到锁的线程就要阻塞。所以同一时间只有一个线程执行,相当于单线程,而单线程的指令重排是没有问题的,也就是宏观上来看,他还有有序执行的
ThreadLocal的内存泄露补充:
参考www.jianshu.com/p/1342a879f…
在介绍原子类中对CAS的补充可见JavaCore和java中CAS详解
java实现多线程的几种方法
1.继承Thread 2 实现Runable接口,并作为Thread的参数
3.实现Callable接口,重写call方法,有返回值,可以处理异常
线程池
AQS
数据库
数据库ACID的C,一致性怎么理解?
ACID里的AID都是数据库的特征,也就是依赖数据库的具体实现.而唯独这个C,实际上它依赖于应用层,也就是依赖于开发者.这里的一致性是指系统从一个正确的状态,迁移到另一个正确的状态.什么叫正确的状态呢?就是当前的状态满足预定的约束就叫做正确的状态.而事务具备ACID里C的特性是说通过事务的AID来保证我们的一致性. 数据库事务一致性的理解
redo log刷盘和持久化的区别
InnoDB引擎为什么用undo log回滚,而不用binlog?
因为binglog记录的是一个完整的事务
一致性非锁定锁/锁定锁存在的原因?
在mysql数据库中,有一个行锁的概念。行锁有两种类型:共享锁(s),排它锁(x);x锁和s锁是不能互相兼容的,而s锁与s锁是可以互相兼容的;在mysql的设计中,在写操作的时候会自动给选定行加上x锁,也就是所谓独占锁,即在没有释放x锁之前,任何会话都不能访问锁定行,我相信这个是没有问题的;但是这样会有问题,别的会话去读加了x锁的行时在正常逻辑下,为了保证数据一致性,我们在读的时候应该需要等待x锁释放再去读,从而可以得到最新最准确的数据,但是这样会严重降低数据库的性能,在数据库有写操作的情况下,读操作都需要等待;那到底在有写操作的时候,读操作要不要去等待呢?欣慰的是:mysql把这个选择权交给了我们,当有写(x锁)操作的情况下,希望读操作等待(加s锁)使用 一致性锁定读,希望读操作不等待(不加s锁)使用非锁定读。
为什么不建议使用外键?
事务的一致性怎么理解?
B+树为什么更合适
最左匹配原则
其他的索引
MVCC可以阻止幻读吗?
比如一个空表,事务A多次select id = 100 返回都是空 这个时候再开一个事务b insert id=100并commit 这个时候事务A再insert id = 100会报错,提示已经存在,因为insert是当前读直接读取最新版本,而不经过ReadView,导致两次数据不一致。但是为什么解决了重复读呢
乐观锁 悲观锁 主从同步
表的划分
Redis
为什么Redis使用单线程
Redis主从复制
Redis集群
zhuanlan.zhihu.com/p/391762630
hash槽和一致性hash的区别
www.jianshu.com/p/4163916a2…
CRC的全称为Cyclic Redundancy Check,中文名称为循环冗余校验。它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制。实际上,除数据通信外,CRC在其它很多领域也是大有用武之地的。例如我们读软盘上的文件,以及解压一个ZIP文件时,偶尔会碰到“Bad CRC”错误,由此它在数据存储方面的应用可略见一斑
redis缓存和数据一致性问题的解决
redis哨兵
bitmap的用法:
bitop and/or dest 20200101 20200102 bitcount dest 统计连续两天都在线或者两天内活跃的用户总量
数据结构
参考小林coding:mp.weixin.qq.com/s/MGcOl1kGu…
简单动态字符串(simple dynamic string,SDS)
压缩列表为了节约空间
压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降
跳表:
跳表的相邻两层的节点数量最理想的比例是 2:1,查找复杂度可以降低到 O(logN)
延时双删
jdbc
jdbc的连接方式
利用driver接口实现类
jdbc:mysql://localhost:3306/test
driver = new com.mysql.jdbc.Driver();
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123");
Connection conn = driver.connect(url, info)
利用注册驱动
DriverManager.registerDriver(driver);
Connection conn = DriverManager.getConnection(url, user, password);
Statement PreparedStatement CallableStatement
在连接到了数据库之后
st = conn.createStatement();
rs = st.executeQuery(sql); // ResultSet
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集的列数
int columnCount = rsmd.getColumnCount();
//Statement 是拼串 存在sql注入
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password+ "'";
//PreparedStatement
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
//4.执行sql语句
ps.execute();
executeUpdate()
-
代码的可读性和可维护性。
-
PreparedStatement 能最大可能提高性能:
- DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
- 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行 代码缓存。这样每执行一次都要对传入的语句编译一次。
- (语法检查,语义检查,翻译成二进制命令,缓存)
-
PreparedStatement 可以防止 SQL 注入
数据库连接池-德鲁伊
public class TestDruid {
public static void main(String[] args) throws Exception {
Properties pro = new Properties(); pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
配置文件
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
通用思路
public void testUpdateWithTx() {
Connection conn = null;
try {
//1.获取连接的操作(
//① 手写的连接:JDBCUtils.getConnection();
//② 使用数据库连接池:C3P0;DBCP;Druid
//2.对数据表进行一系列CRUD操作
//① 使用PreparedStatement实现通用的增删改、查询操作(version 1.0 \ version 2.0)
//version2.0的增删改public void update(Connection conn,String sql,Object ... args){}
//version2.0的查询 public <T> T getInstance(Connection conn,Class<T> clazz,String sql,Object ... args){}
//② 使用dbutils提供的jar包中提供的QueryRunner类
//提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//回滚数据
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
//3.关闭连接等操作
//① JDBCUtils.closeResource();
//② 使用dbutils提供的jar包中提供的DbUtils类提供了关闭的相关操作
}
}