0

1

2

3

4

5

6

7

8

9

0

1

{{ noReadMessageTotal }}

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

尚硅谷Java技术之深圳高频面试题:第五章 JVM相关

晴天 晴天 | 162 | 558天前

尚硅谷Java技术之深圳高频面试题

版本:V1.0

尚硅谷Java技术中心

第五章 JVM相关

1. 谈谈你对JVM的理解

Java虚拟机(JVM)是运行 Java
字节码的虚拟机,为Java程序的运行提供了运行环境。JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的
JVM 实现是 Java 语言"一次编译,随处可以运行"的关键所在。

2. JVM的内存模型

56756.png

0x01:程序计数器(Program Counter Register)

程序计数器(Program Counter
Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机概念模型里(概念模型,各种虚拟机可能会通过一些更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令:分支、跳转、循环、异常处理、线程恢复等基础操作都会依赖这个计数器来完成。每个线程都有独立的程序计数器,用来在线程切换后能恢复到正确的执行位置,各条线程之间的计数器互不影响,独立存储。所以它是一个"线程私有"的内存区域。此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。

0x02:虚拟机栈(VM Stack)

JVM栈是线程私有的内存区域。它描述的是java方法执行的内存模型,每个方法执行的同时都会创建一个栈帧(Stack
Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,都对应着一个栈帧从入栈到出栈的过程。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法。就像是组成动画的一帧一帧的图片,方法的调用过程也是由栈帧切换来产生结果。

局部变量表存放了编译器可知的各种基本数据类型(int、short、byte、char、double、float、long、boolean)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一跳字节码指令的地址)。

在JVM规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

0x03:本地方法栈( Native Method Stack)

本地方法栈和虚拟机栈所发挥的作用是很相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。Sun
HotSpot
直接就把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

0x04:堆(Heap)

Heap是OOM故障最主要的发源地,它存储着几乎所有的实例对象,堆由垃圾收集器自动回收,堆区由各子线程共享使用;通常情况下,它占用的空间是所有内存区域中最大的,但如果无节制地创建大量对象,也容易消耗完所有的空间;堆的内存空间既可以固定大小,也可运行时动态地调整,通过参数-Xms设定初始值、-Xmx设定最大值。

0x05:方法区(Method Area)

方法区是被所有线程共享的内存区域,用来存储已被虚拟机加载的类信息、常量、静态变量、JIT(just
in
time,即时编译技术)编译后的代码等数据。运行时常量池是方法区的一部分,用于存放编译期间生成的各种字面常量和符号引用。

3. 常见的垃圾回收算法有哪些?

1. 复制算法 年轻代中使用的是Minor
GC,这种GC算法采用的是复制算法(Copying)

a)效率高,缺点:需要内存容量大,比较耗内存

b)使用在占空间比较小、刷新次数多的新生区

2. 标记清除 老年代一般是由标记清除或者是标记清除与标记整理的混合实现

a)效率比较低,会差生碎片。

3. 标记压缩 老年代一般是由标记清除或者是标记清除与标记整理的混合实现

a)效率低速度慢,需要移动对象,但不会产生碎片。

4. 标记清除压缩标记清除-标记压缩的集合,多次GC后才Compact

a)使用于占空间大刷新次数少的养老区,是3 4的集合体

4. 了解volatile关键字不?

volatile是Java提供的最轻量级的同步机制,保证了共享变量的可见性,被volatile关键字修饰的变量,如果值发生了变化,其他线程立刻可见,避免出现脏读现象。同时volatile禁止了指令重排,可以保证程序执行的有序性,但是由于禁止了指令重排,所以JVM相关的优化没了,效率会偏弱

5. synchronized的底层实现原理

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit:

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个
monitor 的所有权。

通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,

这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

6. synchronized和volatile有什么区别?

1.volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

2.volatile仅能用在变量级别,而synchronized可以使用在变量、方法、类级别。

3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。

4. volatile不会造成线程阻塞,synchronized可能会造成线程阻塞。

5.volatile标记的变量不会被编译器优化,synchronized标记的变量可以被编译器优化。

7. 如何避免死锁问题?

1. 死锁发生的条件

1) 互斥,共享资源只能被一个线程占用

2) 占有且等待,线程 t1 已经取得共享资源 s1,尝试获取共享资源 s2
的时候,不释放共享资源 s1

3) 不可抢占,其他线程不能强行抢占线程 t1 占有的资源 s1

4) 循环等待,线程 t1 等待线程 t2 占有的资源,线程 t2 等待线程 t1
占有的资源

2. 避免死锁的方法

1) 对于以上 4 个条件,只要破坏其中一个条件,就可以避免死锁的发生。

2) 对于第一个条件 “互斥” 是不能破坏的,因为加锁就是为了保证互斥。

3) 其他三个条件,我们可以尝试

a) 一次性申请所有的资源,破坏 “占有且等待” 条件

b)
占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏
“不可抢占” 条件

c) 按序申请资源,破坏 “循环等待” 条件

3. 编程中的最佳实践:

1) 使用 Lock 的 tryLock(long timeout, TimeUnit
unit)的方法,设置超时时间,超时可以退出防止死锁

2) 尽量使用并发工具类代替加锁

3) 尽量降低锁的使用粒度

4) 尽量减少同步的代码块

相关文章:
第一章 Java基础
第二章 MySQL相关
第三章 框架相关
第四章 Redis相关
第五章 JVM相关
第六章 消息队列相关
第七章 项目相关

文章标签: Jvm

真诚点赞 诚不我欺~

{{ praiseUserVoList.length }}人点赞

item.nickname

尚硅谷Java技术之深圳高频面试题:第五章 JVM相关

{{ isPraise ? '已点赞' : '点赞'}}
{{ isCollect ? '已收藏' : '收藏'}}
评论
gOod mornIng
没有更多啦~ 加载中...

关于作者

晴天
晴天

人因梦想而伟大!