1.请你谈谈CMS垃圾回收器
垃圾回收器组合
垃圾回收器从线程运行情况分类有三种:
(1)串行回收,Serial回收器,单线程回收,全程stw;
(2)并行回收,名称以Parallel开头的回收器,多线程回收,全程stw;
(3)并发回收,cms与G1,多线程分阶段回收,只有某阶段会stw;
CMS垃圾回收
CMS垃圾回收特点
(1)CMS只会回收老年代和永久代(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻代;
(2) CMS是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以CMS垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久代达到92%;
CMS垃圾回收器工作原理
CMS的GC过程有6个阶段(4个并发,2个暂停(初次标记,重新标记)其它应用程序):
- 初次标记(STW initial mark):标记老年代中所有的GC Roots引用的对象;标记老年代中被年轻代中活着的对象引用的对象(初始标记也会扫描新生代);会导致stw。
- 并发标记(Concurrent marking):从初次标记收集到的‘根’对象引用开始,遍历所有能被引用的对象。
- 并发可中断预清理(Concurrent precleaning):改变当运行第二阶段时,由应用程序线程产生的对象引用,以更新第二阶段的结果。标记在并发标记阶段引用发生变化的对象,如果发现对象的引用发生变化,则JVM会标记堆的这个区域为Dirty Card。那些能够从Dirty Card到达的对象也被标记(标记为存活),当标记做完后,这个Dirty Card区域就会消失。
- 最终重新标记(STW remark):由于并发预处理是并发的,对象引用可能发生进一步变化。因此,应用程序线程会再一次被暂停(stw)以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。这一阶段十分重要,因为必须避免收集到仍被引用的对象。
- 并发清理(Concurrent sweeping):清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。
- 并发重置(Concurrent reset):CMS清除内部状态,为下次回收做准备。
补充:
Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
详细解释,请参考:https://www.jianshu.com/p/08f0b85ad665
2.Java的接口和C++的虚类的相同和不同处
C++虚类相当于java中的抽象类,与接口的不同处是:
1.一个子类只能继承一个抽象类(虚类),但能实现多个接口
2.一个抽象类可以有构造方法,接口没有构造方法
3.一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有方法声明
4.一个抽象类可以是public、private、protected、default,接口只有public
5.一个抽象类中的方法可以是public、private、protected、default,接口中的方法只能是public和default修饰,实际上都是public的abstract方法
相同之处是:
都不能实例化。
补充:
接口是一类特殊的抽象类,是更抽象的抽象类,你可以这样理解。抽象类是一个不完整的类,接口只定义了一些功能。
3.为什么重写equals还要重写hashcode?
解释问题前,我们先来看看这个问题:
请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的?
Object 的 hashcode 方法是本地方法,也就是用 c 或 c++ 实现的,该方法直接返回对象的内存地址。
如果没有重写hashCode(),则任何对象的hashCode()值都不相等(而hashMap想让部分值的hashCode值一样,所以就要重写)
由上面问题解释我们知道没有重写hashCode(),则任何对象的hashCode()值都不相等。
HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。
如果只重写equals没有重写hashCode(),就会导致相同的key值也被hashcode认为是不同的key值(因为没有重写hashCode(),则任何对象的hashCode()值都不相等),就会在hashmap中存储相同的key值(map中key值不能相同),这就不符合条件了。
equals和hashcode的关系:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
4.链表中环的入口结点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
示例代码:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
}
}
思路:
首先判断链表中是否有环 思路是用两个指针,同时从链表的节点出发
一个走的慢,一个走的快
如果两个指针不能相遇则无环,否则有环
如何找到环的入口?
还是先定义两个指针,p1,p2
n代表链表中环的节点个数
p1先向前移动n步
然后两个指针以相同的速率向前移动
两个指针相遇的地方就是环的入口地址(总结规律出来的)
接下来就是如何求环中节点的个数
两个指针相遇的地方一定是在环的内部
所以可以从这个节点出发,边走边计数
当再次回到这个节点的时候 就知道环中节点的个数了
实现代码:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
//Return the meet node
ListNode meetNode = meetingNode(pHead);
if(meetNode==null) return null;
//the length of loop
int countOfLinklistLoop=0;
//To Traverse the loop
ListNode node1=meetNode.next;
while(meetNode!=node1){
countOfLinklistLoop++;
node1=node1.next;
}
//p1 move the length of the loop
for(int i=0;i<countOfLinklistLoop;i++){
node1=node1.next;
}
//the two node move at the same time,the meet location is the loop start location
ListNode node2=pHead;
while(node2!=node1){
node1=node1.next;
node2=node2.next;
}
//To find the start loop location successfully
return node1;
}
//Determine whether there is a part of the linklist and find the meet node
ListNode meetingNode(ListNode pHead){
if(pHead==null) return null;
ListNode slowNode=pHead;
ListNode fastNode=pHead.next;
while(slowNode!=null&&fastNode!=null){
//To find the success
if(fastNode==slowNode){
return fastNode;
}
//To find the failure currently,continue to finding
fastNode=fastNode.next;
slowNode=slowNode.next;
//Move to determine ahead of time
if(fastNode!=null){
fastNode=fastNode.next;
}
}
//To find the failure
return null;
}
}