JVM常见面试题解析(1)

2)指令重排序。

在这里,先看一段代码

public class PossibleReordering {

static int x = 0, y = 0;

static int a = 0, b = 0;

public static void main(String[] args) throws InterruptedException {

Thread one = new Thread(new Runnable() {

public void run() {

a = 1;

x = b;

}

});

Thread other = new Thread(new Runnable() {

public void run() {

b = 1;

y = a;

}

});

one.start();other.start();

one.join();other.join();

System.out.println(“(” + x + “,” + y + “)”);

}

运行结果可能为(1,0)、(0,1)或(1,1),也可能是(0,0)。因为,在实际运行时,代码指令可能并不是严格按照代码语句顺序执行的。大多数现代微处理器都会采用将指令乱序执行(out-of-order execution,简称OoOE或OOE)的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待3。通过乱序执行的技术,处理器可以大大提高执行效率。而这就是指令重排。

3)内存屏障

内存屏障,也叫内存栅栏,是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。

  • LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

  • StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

  • LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

  • StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。 在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

4)happen-before原则
  • 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。

  • 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。

  • volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。

  • happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。

  • 线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。

  • 线程中断的happen-before原则 :对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。

  • 线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。

  • 对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。

7.简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。

思路: 先说明一下什么是类加载器,可以给面试官画个图,再说一下类加载器存在的意义,说一下双亲委派模型,最后阐述怎么打破双亲委派模型。

我的答案:
1) 什么是类加载器?

类加载器 就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。

  • 启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<java_home style=“margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;”>\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。</java_home>

其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:

  • 扩展类加载器(Extension ClassLoader):负责加载<java_home style=“margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;”>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。</java_home>

  • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

2)双亲委派模型
双亲委派模型工作过程是:

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

双亲委派模型图:

3)为什么需要双亲委派模型?

在这里,先想一下,如果没有双亲委派,那么用户是不是可以自己定义一个java.lang.Object的同名类,java.lang.String的同名类,并把它放到ClassPath中,那么类之间的比较结果及类的唯一性将无法保证,因此,为什么需要双亲委派模型?防止内存中出现多份同样的字节码

4)怎么打破双亲委派模型?

打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法。

8.说说你知道的几种主要的JVM参数

思路: 可以说一下堆栈配置相关的,垃圾收集器相关的,还有一下辅助信息相关的。

我的答案:

1)堆栈配置相关

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

-XX:MaxPermSize=16m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxTenuringThreshold=0

  • -Xmx3550m:最大堆大小为3550m。

  • -Xms3550m:设置初始堆大小为3550m。

  • -Xmn2g:设置年轻代大小为2g。

  • -Xss128k:每个线程的堆栈大小为128k。

  • -XX:MaxPermSize: 设置持久代大小为16m

  • -XX:NewRatio=4: 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。

  • -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

  • -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。

2)垃圾收集器相关

-XX:+UseParallelGC

-XX:ParallelGCThreads=20

-XX:+UseConcMarkSweepGC

-XX:CMSFullGCsBeforeCompaction=5

-XX:+UseCMSCompactAtFullCollection:

  • -XX:+UseParallelGC:选择垃圾收集器为并行收集器。

  • -XX:ParallelGCThreads=20:配置并行收集器的线程数

  • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。

  • -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

3)辅助信息相关

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGC 输出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs]

[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails 输出形式:

[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs

9.怎么打出线程栈信息。

思路: 可以说一下jps,top ,jstack这几个命令,再配合一次排查线上问题进行解答。

我的答案:
  • 输入jps,获得进程号。

  • top -Hp pid 获取本进程中所有线程的CPU耗时性能

  • jstack pid命令查看当前java进程的堆栈状态

  • 或者 jstack -l > /tmp/output.txt 把堆栈信息打到一个txt文件。

  • 可以使用fastthread 堆栈定位,fastthread.io/

10.强引用、软引用、弱引用、虚引用的区别?

思路: 先说一下四种引用的定义,可以结合代码讲一下,也可以扩展谈到ThreadLocalMap里弱引用用处。

我的答案:
1)强引用

我们平时new了一个对象就是强引用,例如 Object obj = new Object();即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。

2)软引用

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。

SoftReference softRef=new SoftReference(str); // 软引用

用处:

软引用在实际中有重要的应用,例如浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。(1)如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建(2)如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出

如下代码:

Browser prev = new Browser(); // 获取页面进行浏览

SoftReference sr = new SoftReference(prev); // 浏览完毕后置为软引用

if(sr.get()!=null){

rev = (Browser) sr.get(); // 还没有被回收器回收,直接获取

}else{

prev = new Browser(); // 由于内存吃紧,所以对软引用的对象回收了

sr = new SoftReference(prev); // 重新构建

}

3)弱引用

具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

String str=new String(“abc”);

WeakReference abcWeakRef = new WeakReference(str);

str=null;

等价于

str = null;

System.gc();

4)虚引用

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

參考与感谢
  • 为什么新生代内存需要有两个Survivor区

  • JVM内存模型、指令重排、内存屏障概念解析

  • java 8大happen-before原则超全面详解

  • JVM参数配置大全

  • Java 的强引用、弱引用、软引用、虚引用

  • 《深入理解Java虚拟机》

Android开发资料+面试架构资料 免费分享 点击链接 即可领取

《Android架构师必备学习资源免费领取(架构视频+面试专题文档+学习笔记)》

总结

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
们去适应环境,而不是环境来适应我们!**

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-KhJ9hOfi-1715843051411)]

[外链图片转存中…(img-9n4p1anw-1715843051412)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值