java面经整理(5)

来源:互联网 发布:怎么样提高淘宝流量 编辑:程序博客网 时间:2024/06/06 03:53
https://www.nowcoder.com/discuss/27285?type=2&order=3&pos=75&page=1
一.jvm内存模型与垃圾回收方法


1.程序计数器(The Program Counter Register)
多线程时,当线程数超过CPU数量或CPU内核数量,线程之间就要根据时间片轮询抢夺CPU时间资源。因此每个线程有要有一个独立的程序计数器,记录下一条要运行的指令。
2.虚拟机栈 (Java Virtual Machine Stacks)
线程私有的,与线程在同一时间创建。管理JAVA方法执行的内存模型。每个方法执行时都会创建一个桢栈来存储方法的私有变量、操作数栈、动态链接方法、返回值、返回地址等信息。
3.本地方法区(Native Method Stacks)
和虚拟机栈功能相似,但管理的不是JAVA方法,是本地方法,本地方法是用C实现的。
4.JAVA堆(JVM heap)
线程共享的,存放所有对象实例和数组。垃圾回收的主要区域。可以分为新生代和老年代(tenured)。
新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存得足够长,老年对象就会被移入老年代。
新生代又可进一步细分为eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。刚创建的对象都放入eden,s0和s1都至少经过一次GC并幸存。如果幸存对象经过一定时间仍存在,则进入老年代(tenured)。
5.方法区( Method Area) 
线程共享的,用于存放被虚拟机加载的类的元数据信息:如常量、静态变量、即时编译器编译后的代码。也称为永久代


垃圾回收算法


1.标记-清除算法(Mark-Sweep)
从根节点开始标记所有可达对象,其余没标记的即为垃圾对象,执行清除。但回收后的空间是不连续的。


2.复制算法(copying)
将内存分成两块,每次只使用其中一块,垃圾回收时,将标记的对象拷贝到另外一块中,然后完全清除原来使用的那块内存。复制后的空间是连续的。复制算法适用于新生代,因为垃圾对象多于存活对象,复制算法更高效。在新生代串行垃圾回收算法中,将eden中标记存活的对象拷贝未使用的s1中,s0中的年轻对象也进入s1,如果s1空间已满,则进入老年代;这样交替使用s0和s1。这种改进的复制算法,既保证了空间的连续性,有避免了大量的内存空间浪费。
3.标记-压缩算法(Mark-compact)
适合用于老年代的算法(存活对象多于垃圾对象)。
标记后不复制,而是将存活对象压缩到内存的一端,然后清理边界外的所有对象。


二.括号匹配算法
假设表达式中允许包含两种括号:圆括号和方括号,其嵌套顺序随意,及([]())或[([][])]等均为正确的格式,[(])或([())或(()]均为不正确的格式。


匹配算法的思想是:


首先将第一个括号压入栈,然后从第二个括号开始,如果与栈顶元素能匹配,能将栈顶元素弹出;如果不匹配,则将该元素压入栈中。


当带匹配字符串遍历结束后,检查栈是否为空,为空则表示匹配成功了,如果非空则表示还有括号未能匹配,即该字符串匹配失败。
public class BracketMatch {
    /**
     * 进行匹配的算法。
     * @param str 待检查的字符串。
     * @return
     */
    public static boolean match(String str) {
        Stack stack = new Stack(); // 定义一个存放括号的栈。
        char[] ca = str.toCharArray(); // 将字符串转为字符数组以便对其遍历。
        stack.push((Character) ca[0]); // 首先将第一个字符压入栈中。
        /*
         * 从第二个字符开始,依次与栈中字符匹配。
         * 成功则将栈顶元素弹出。
         * 失败则将字符数组中的当前字符压入栈中。
         */
        for (int index = 1; index < ca.length; ++index) {
            Character c1 = (Character) stack.peek();
            Character c2 = ca[index];
            if ((c1.equals('(') && c2.equals(')'))
                    || (c1.equals('[') && c2.equals(']'))) {
                stack.pop();
            } else {
                stack.push(c2);
            }
        }
        return stack.empty();
    }
}
三.写出线程安全的单例模式
单例模式是为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种模式方法。
从概念中体现出了单例的一些特点:
(1)、在任何情况下,单例类永远只有一个实例存在
(2)、单例需要有能力为整个系统提供这一唯一实例  
1.多线程安全单例模式实例一(不使用同步锁)
public class Singleton {
    private static Singleton sin=new Singleton();    ///直接初始化一个实例对象
    private Singleton(){    ///private类型的构造函数,保证其他类对象不能直接new一个该对象的实例
    }
    public static Singleton getSin(){    ///该类唯一的一个public方法    
        return sin;
    }
}
上述代码中的一个缺点是该类加载的时候就会直接new 一个静态对象出来,当系统中这样的类较多时,会使得启动速度变慢 。现在流行的设计都是讲“延迟加载”,我们可以在第一次使用的时候才初始化第一个该类对象。所以这种适合在小系统。 


2.多线程安全单例模式实例二(使用同步方法)


public class Singleton {  
     private static Singleton instance;  
     private Singleton (){
         
     }   
     public static synchronized Singleton getInstance(){    //对获取实例的方法进行同步
       if (instance == null)     
         instance = new Singleton(); 
       return instance;
     }
 }
上述代码中的一次锁住了一个方法, 这个粒度有点大 ,改进就是只锁住其中的new语句就OK。就是所谓的“双重锁”机制。
3.多线程安全单例模式实例三(使用双重同步锁)
public class Singleton {  
     private static Singleton instance;  
     private Singleton (){
     }   
     public static Singleton getInstance(){    //对获取实例的方法进行同步
       if (instance == null){
           synchronized(Singleton.class){
               if (instance == null)
                   instance = new Singleton(); 
           }
       }
       return instance;
     }
     
 }
原创粉丝点击