今日面经2
hashmap中hash的计算
链接:https://www.cnblogs.com/mxxct/p/13857097.html
- hash 计算
JDK1.8
HashMap源码
1 | static final int hash(Object key) { |
右移16位相当于将高16位移入到低16位,再与原hashcode做异或计算(位相同为0,不同为1)可以将高低位二进制特征混合起来 => 高16位没有发生变化,但是低16位改变了
拿到的hash值会参与hashmap中数组槽位的计算,计算公式:(n - 1) & hash。
高区的16位很有可能会被数组槽位数的二进制码锁屏蔽,如果我们不做刚才移位异或运算,那么在计算槽位时将丢失高区特征
虽然丢失了高区特征,不同hashcode也可以计算出不同的槽位来,但是如果两个hashcode很接近时,高区的特征差异可能会导致一次哈希碰撞。
- 使用异或运算的原因
异或运算能更好的保留各部分的特征,如果采用 & 运算计算出来的值会向0靠拢,采用 | 运算计算出来的值会向1靠拢
- 为什么槽位数必须使用2^n / 为什么要 &length-1
为了让哈希后的结果更加均匀,减少hash碰撞
- 扩容后Hash值计算
length * 2,即新增的bit位是1,在 (n - 1) & hash 时,只需要判断新增加的这一个bit位,如果是0的话,说明索引不变,如果变成1了,索引变成 原索引+扩容前的容量大小
gc对象生命周期
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为
三个区域:Eden、From Survivor、To Survivor。
这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
老年代:
老年代主要存放在程序中生命周期长的对象。老年代因为其中对象比较稳定,所以Major GC不会频繁的执行。在进行Major GC之前通常都会先执行一次Minor GC,Minor GC执行完后可能会有新生代的对象晋升到老年代之中,然后导致老年代的空间不足才触发Major GC。
永久代:java7之前
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
java8以后是元空间。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小。
gc判断对象存活的方法
引用计数
- 优点:实现简单
- 缺点:难以解决对象循环引用的问题
可达性分析
- 通过一系列的称为“GC Roots”的对象作为起始点,从这些节点考试向下探索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
gc root包括哪些?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象;
- 方法区中类静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
给你5张牌,其中包含两张大小王,判断是否是顺子
1 | class Solution { |
链表反转
1 | class Solution { |
二叉树的遍历
前中后序递归遍历
1 | /** |
层序遍历
1 | /* |
前中后序非递归遍历
1 | /** |