minds
稳如老狗,才能赢
Talk is cheap, show me the code.
提示:身体和思想要保持一致,想法不是事实
一、面经
1.JVM
JVM类加载机制是什么样子的呢?
- 程序会编译成字节码文件,类加载就是将这个字节码文件,加载到JVM内存中,生成一个可以被JVM直接使用的Java类型;
- 类加载分为三个阶段,分别是:加载、连接、初始化;
- 加载就是将字节码文件,加载进入JVM内存中,然后在方法区中生成一个Class对象,可以直接调用这个类的各种信息,比如反射机制;
- 连接又分为三个阶段:
- 验证:确保这个字节码文件的各种信息文件格式是正确的,不会危害到虚拟机本身的安全;
- 准备:准备阶段就是将类里面被static修饰的各种变量进行一个初始化;
- 解析:将类中的符号引用变为直接引用;
- 初始化:这才是真正开始执行我们写的程序代码的地方,会将类中的各种静态变量静态代码块放到一起,执行clinit方法;
JVM中哪些地方可能会内存溢出?什么场景会导致内存溢出呢?
- 只有程序计数器不会发生内存溢出,其余的所有数据运行时区域都可能发生内存溢出;
- 如果内存中加载了特别大的数据,比如说一次性从数据库中去取出了很多条数据;
- 代码中存在死循环,或者一直循环使用比较大的对象;
- 长期的内存泄漏导致了内存溢出;
如果新生代和老年代的所有内存只有100M,但是定义了一个200M的对象,会内存溢出吗?
- 根据jvm的对象内存分配策略,大对象优先进入老年代进行分配,如果发现这个对象不能进入老年代,就会触发一次FULLGC;
- 然后再去分配这个对象,发现内存不够,会导致内存溢出;
2.Redis
如果redis集群中的主服务器突然宕机了,那么怎么从从服务器中选出一个主节点呢?选举机制是什么样子的?
- 如果这个redis集群是主从架构的,那么主节点宕机下线了,不会从从节点中选举出来一个主节点;
- 如果这个redis集群是哨兵架构的,那么哨兵集群会负责从从节点中选举出来一个从节点作为新的主节点;
- 选举机制是:
- 首先要判断这个主节点是主观下线了,再确定一下客观也下线了后;
- 在哨兵集群中,选出一个主sentinel节点;
- 主sentinel节点根据slave节点中的优先级选出优先级最高的节点作为新的redis集群中的主节点;
- 如果slave中各个节点的优先级一致,那么会选择复制偏移量最大的slave作为新的主节点,或者选择每次启动时随机生成的runid最小的作为主节点;
redis数据类型?分别使用场景是什么?
- redis总共有五种常见的和三种不太常见的数据类型;
- 五种常见的为:
- string,比如在给用户进行短信验证码校验的时候,会将短信的验证码存入redis中;
- hash,比如需要存储用户信息;
- list,存储文章列表;
- sorted set,进行排序的,比如最先点赞的;
- set,进行去重,比如共同关注的好友;
- 三种不太常见的:
- bit,位图,比如实现打卡机制;
- hyperloglogs,没用过;
- geo,地理位置的信息;
redis中的 z set 结构有过了解吗?使用场景是什么?
- 这是一个排序的去重集合,可以实现按照一定的规则进行排序;
- 我在项目中使用sorted set存储最先点赞帖子的用户id,根据点赞时间顺序去显示用户头像;
3.Linux
如果我在linux里面想要查看一个日志文件,大致会用到什么指令?
- touch,创建一个日志文件;
- cat,查看这个日志文件;
- vim,操作修改这个日志文件,其中还用到了:
- i,插入;
- gg,直接到尾部;
- shift+g,直接到达首部;
- wq,保存并退出;
- !,强制退出
- rm,删除日志文件;
4.MySQL
MySQL的存储引擎介绍一下?
- 在MySQL中,目前默认的存储引擎是innoDB,它主要的特点是三个,支持事务、外键和行级锁;
- 在MySQL中,还有MyISAM和Memory两个存储引擎;
- myisam和innodb的最大区别就是不支持事务、外键和行级锁,但是myisam的查询速度比innodb要快,在一些对于数据一致性要求不严格的场景,比如查询评论,就可以使用myisam;
- memory是一个基于内存的数据存储引擎,只能作为临时表或者缓存使用,采用的是hash索引结构;
为什么MySQL使用B+树索引结构,不采用二叉树、红黑树或者B树呢?
- 对于二叉树来说,顺序插入数据的时候很有可能成为一个链表,造成查询效率极低,并且二叉树只有左右两个子节点,数据量大的时候,树的层级会很深;
- 红黑树虽然不会出现链表这种极端情况,但是还是会出现层级很深的情况,造成查询效率低;
- B树的每个节点上都存储了数据,那么每张页上存储的数据和指针都会比B+树少,树的层级也会比B+树深,并且B树的叶子节点没有指针指向,范围查询没有B+树快;
二、力扣
1.包含min函数的栈
思路:
- 创建两个栈,一个栈只记录每次加入到栈中的元素;
- 另外一个栈记录,每次比这个栈顶更小的元素加入到栈中;
代码如下:
class MinStack {
Stack<Integer> stack;
Stack<Integer> help;
/** initialize your data structure here. */
public MinStack() {
// initialization
stack = new Stack<>();
help = new Stack<>();
}
public void push(int x) {
stack.push(x);
if(help.isEmpty() || help.peek() >= x){
help.push(x);
}
}
public void pop() {
if(stack.pop().equals(help.peek())){
help.pop();
}
}
public int top() {
return stack.peek();
}
public int min() {
return help.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
2. 栈的压入、弹出序列
思路:
- 要比较第二个数组是否为第一个数组的弹出栈序列,那么就借助一个栈进行辅助比较;
代码如下:
class Solution {
// 判断第二个数组是否为第一个数组弹出栈的顺序
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();
int j = 0;
for(int push : pushed){
stack.push(push);
while(!stack.isEmpty() && stack.peek() == popped[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
}
3.序列化二叉树
思路:
- 利用层序遍历,将每一层的节点加入到队列中;
代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null){
return "[]";
}
StringBuilder sb = new StringBuilder("[");
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode head = queue.poll();
if(head != null){
sb.append(head.val + ",");
queue.add(head.left);
queue.add(head.right);
}else{
sb.append("null,");
}
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")){
return null;
}
String[] array = data.substring(1, data.length() - 1).split(",");
Queue<TreeNode> queue = new LinkedList<>();
TreeNode root = new TreeNode(Integer.parseInt(array[0]));
queue.add(root);
int i = 1;
while(!queue.isEmpty()){
TreeNode head = queue.poll();
if(!array[i].equals("null")){
head.left = new TreeNode(Integer.parseInt(array[i]));
queue.add(head.left);
}
i++;
if(!array[i].equals("null")){
head.right = new TreeNode(Integer.parseInt(array[i]));
queue.add(head.right);
}
i++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
总结
每天都要学习一点点