Java基础面试题

来源:互联网 发布:pm4十字绣软件 编辑:程序博客网 时间:2024/06/03 14:42

====================================线程==========================================================


1.为什么会有线程不安全?

答:因为方法在执行的时候,为了方便回从堆里面将变量copy到栈中,因为栈是线程私有的,所以导致了数据的不一致。



2.线程安全的三要素是什么?
答:可见性,原子性,顺序性。


3.volatile是如何保证线程的可见性的?它跟final有什么区别?
答:volatile修饰变量时,强制各个线程从主内容空间去取变量,从而实现了线程的可见性。
final修饰的变量不可变,所以不存在每个线程上面的当前对象的不一致的可能性。




4.synchronized 和lock原理?
答:synchronized是对象锁,它是通过monitorentry和monitorexit来监控被锁的对象。
lock的核心原理是ASQ和LockSupport,asq的原理是cas (compore and swap)


5.cas 的原理是?
答:cas是根据里面的一个标志位status来判断是否锁被占用了。
类似于mysql的乐观锁


6.happens-before的原则?是用来解决什么问题的?
答:
1:一个线程的A操作总是在B之前,那多线程的A操作肯定实在B之前。
2:monitor 再加锁的情况下,持有锁的肯定先执行。
3:volatile修饰的情况下,写先于读发生
4:线程启动在一起之前 strat
5:线程死亡在一切之后 end
6:线程操作在一切线程中断之前
7:一个对象构造函数的结束都该对象的finalizer的开始之前
8:传递性,如果A肯定在B之前,B肯定在C之前,那A肯定是在C之前。

是用来解决重排序的问题的。因为jvm虚拟机允许在不影响代码最终结果的情况下,可以乱序执行。



====================================集合==========================================================


1.hashmap的数据结构?
答:数组+链表
数组里面放的是链表的第一个值
链表放的是hash后产生冲突的值


2.hashmap.put(a,b)做了那几个操作?
答:首先计算a的hashing值,然后根据a的hashing值找到a所对象的桶(busket),取出当前桶里面所存放的链表,遍历链表,看是否存在key为a的对象,如果有,就将原来的值返回,并覆盖上新值b。最后将entry加入到链顶。


3.为什么hashmap是无序的?
答:因为hashmap的默认容量是16,负载因子是0.75。就是当hashmap填充了75%的busket是就会扩容,最小的可能性是(16*0.75),一般为原内存的2倍,然后rehash,这时候导致了hashmap的无序性。


4.concurrentHashMap和hashtable的区别
hashtable是将整个类加了锁,所以整个类的操作性能都比较低,而concurrentHashMap是在busket上面加了锁,实现了锁的细化,可以同时支持16个线程的并发。


5.concurrentHashMap是如何保证线程安全的
1.就是key和entry都是用final修饰的,从而保证了结构的不可变形
2.value是用volatile修饰的,从而保证了线程的可见性
3.busket操作的时候加了锁(reentrantlock)


====================================虚拟机========================================================

1.jvm虚拟机的内存模型?
答:jvm内存模型分两种类型:
一种是线程安全的:虚拟机栈,本地方法栈,程序计数器
另外一种是非线程安全的:堆(新生代,老年代),方法区(永久带是HotSpot对方法区的一种实现),在jdk8的版本中方法区被元空间去取代。


堆:堆是所有的对象创建和分配的空间,也是GC发生最频繁的空间。
方法区:是用来存放所有编译完成的类的方法和常量信息。
虚拟机栈:每个方法被调用的时候都会创建一个栈空间,用来存放方法的局部变量、操作方法和方法返回地址
本地方法栈:与虚拟机栈类似,不过调用的是native修饰的非java语言的方法
程序计数器:是当前线程所执行的class文件的行号指示器。字节码通过改变计算器的值来选取下一条要执行的字节码指令,分支,循环,异常处理。


2.对象回收的算法,GC收集器?
答:常见GC收集器的算法有可达性分析和引用计数。
引用计数是增加一个字段来标识当前的引用次数,引用计数为0的就是可以GC的。但是引用计数不能解决循环引用的问题
可达性分析:就是通过一系列GC ROOT的对象作为起点,向下搜索,搜索所有没有与当前对象GC ROOT 有引用关系的对象。这些对象就是可以GC的。


常见的GC ROOT:
1.虚拟机栈中引用的对象
2.方法区中静态属性修饰的对象
3.方法区中的常量
3.本地方法引用的对象


常见的垃圾收集器有:串行垃圾收集器,并行垃圾收集器,CMS,G1




3.CMS和G1的区别
CMS是并发标记垃圾收集器,他的主要步骤有:初始收集,并发标记,重新标记,并发清除(删除),重置
G1的主要步骤有:初始标记,并发标记,重新标记,复制清除(整理)


CMS的缺点是对cpu的要求比较高。G1是将内存化成了多块,所有对内段的大小有很大的要求
CMS是请求,所以会存在很多的内存碎片。G1是整理,所以碎片空间较小




4.什么情况下会触发minor gc,什么时候会触发major gc?
堆要创建一个对象A是,首先看年轻代是否有足够的空间,如果没有足够的空间就触发minor gc,如果minor gc后还是没有足够的空间存放当前对象,就判断幸存区是否有足够的空间,如果有就将新生代的一些存活的对象移到幸存区中,并在新生代创建A。如果没有就判断老年代是否有足够的空间,如果有就将幸存区的对象移到老年区,并将新生代的部分存活对象移到幸存区,然后在新生代创建对象A。如果如果老年代也没有,就触发major gc。gc后如果拥有空间后,就将幸存区的对象移到老年区,并将新生代的部分存活对象移到幸存区,然后在新生代创建对象A,如果还是没有就抛出OOM异常。


5.类的生命周期?
装载,校验,准备,解析,初始化,调用,销毁。


装载:是将.java文件解析成.class文件
校验:校验.class文件是否符合jvm规范
准备:给类的静态变量和基本类型创建空间
解析:是将间接引用改为直接引用
初始化:对类的静态变量、基本类型和静态代码块执行初始化操作


6.类加载机制?
全盘委派:当加载器负责一个类的加载,那这个类所引用的其他类也由当前加载器加载
父类委派:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载的class文件都会被缓存,只有在缓存里面找不到当前class,才回去重新读取该类的二进制流

0 0