1.请你谈谈CMS垃圾回收器

垃圾回收器组合

java调用c接口中文乱码_两个指针

垃圾回收器从线程运行情况分类有三种:
(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个暂停(初次标记,重新标记)其它应用程序):

  1. 初次标记(STW initial mark):标记老年代中所有的GC Roots引用的对象;标记老年代中被年轻代中活着的对象引用的对象(初始标记也会扫描新生代);会导致stw。
  2. 并发标记(Concurrent marking):从初次标记收集到的‘根’对象引用开始,遍历所有能被引用的对象。
  3. 并发可中断预清理(Concurrent precleaning):改变当运行第二阶段时,由应用程序线程产生的对象引用,以更新第二阶段的结果。标记在并发标记阶段引用发生变化的对象,如果发现对象的引用发生变化,则JVM会标记堆的这个区域为Dirty Card。那些能够从Dirty Card到达的对象也被标记(标记为存活),当标记做完后,这个Dirty Card区域就会消失。
  4. 最终重新标记(STW remark):由于并发预处理是并发的,对象引用可能发生进一步变化。因此,应用程序线程会再一次被暂停(stw)以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。这一阶段十分重要,因为必须避免收集到仍被引用的对象。
  5. 并发清理(Concurrent sweeping):清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。
  6. 并发重置(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;
    }
}