1.JVM内存模型?
2.如何判断一个对象是不是垃圾?
引用计数算法
一个对象每被引用一次,则计数加一,每次取消引用,则计数减一,最后计数为0的则判断为垃圾。
可达性分析算法
从根节点(GC Root)出发,遍历与之相关联的所有引用,对于不可达的引用,当做垃圾处理。
3.垃圾回收算法?
标记-清除
首先标记哪些对象在用,哪些对象是垃圾,然后把垃圾进行清除。这种方式会产生大量的碎片空间,容易造成内存足够,但是放不下大文件的情况。
标记-复制
内存空间一分为二,每次只用其中一个,标记完哪些对象在用,哪些是垃圾之后,把那些还在被引用的对象都复制到另一半内存空间,然后清空当前内存空间的所有垃圾。这种方式效率极高,适用于对象存活率较低的情况,而年轻代的对象有着朝生夕死的特点,所以一般年轻代的垃圾收集器一般采用的都是复制算法。
标记-整理
首先标记哪些对象在用,哪些对象已经变成垃圾,然后把在用的对象朝某一段进行移动,然后再删除垃圾,这样就不会产生碎片空间,但是效率很低。
4.垃圾收集器?
5.JVM调优?
所谓的jvm调优指的是如何减少gc的次数,减少full gc 次数尤为重要
6.JAVA内存模型?
7.JAVA内存模型底层八大原子操作?
lock:对主内存中的共享变量进行加锁
read:把共享变量从主内存中读出来
load:把从主内存读出来的共享变量加载到工作内存中
use:线程中的业务使用工作内存中的共享变量副本
assign:线程中业务产生的结果赋值给工作内存中的变量
store:把工作内存中的变量存入主内存
write:把读入主内存的变量值赋值给主内存中的变量
unlock:对主内存中的共享变量进行解锁
8.CPU的缓存一致性?
9.指令重排?
cpu,内存,jvm在执行指令的时候,会对指令的执行顺序进行优化,从而导致指令乱序。
那么指令重排遵循两个原则:
1.as-if-serial
单线程的情况下,如果指令重排并不影响最终的执行结果,那么是允许重排的。
2.happens-before
这个原则规定了某些指令必须在另一些指令之前执行。
10.DCL(double check lock 双重检测锁)指令重排问题?
public class DoubleCheckLockSingle {
private static volatile DoubleCheckLockSingle doubleCheckLockSingle;
public static DoubleCheckLockSingle getDoubleCheckLockSingle() {
if (doubleCheckLockSingle == null) {
synchronized (DoubleCheckLockSingle.class) {
if (doubleCheckLockSingle == null) {
doubleCheckLockSingle = new DoubleCheckLockSingle();
}
}
}
return doubleCheckLockSingle;
}
}
以DCL单例模式为例,关键点有两个:
1.实例前加了volatile修饰
doubleCheckLockSingle = new DoubleCheckLockSingle();
这行代码编译成字节码如下:
new #3 <xu/ba/dou/DoubleCheckLockSingle>
dup
invokespecial #4 <xu/ba/dou/DoubleCheckLockSingle.<init>>
由于这三条指令在单线程下,不管什么顺序,都不会影响结果,所以是存在指令重排的可能,所以添加volatile修饰,保证初始化过程不会出现指令重排
2.同步代码块里再次进行非空判断
再次进行非空判断是因为如果,有多个线程都进入到了第一个if代码块内,那么就会出现多次重复初始化实例的现象
11.JVM内存屏障?
loadload:
storestore
loadstore
storeload