JVM面试常考

JVM面试探究:

1、请你谈谈对jvm的理解?java8虚拟机和之前的变化更新?

2、什么是OOM(内存溢出)?什么是栈溢出StackOverFlowError?怎么分析?

3、JVM的常用调优参数有哪些?

4、内存快照如何抓取,怎么分析Dump文件?是否知道?

5、谈谈JVM中的类加载器你的认识?

1.JVM的位置
在操作系统之上运行的软件,JVM是用C写的

2.JVM的体系结构

在这里插入图片描述

在这里插入图片描述

3.类加载器
作用:加载class文件
在这里插入图片描述
类加载器的种类:
1.根加载器
2.扩展类加载器
3.应用程序类加载器

4.双亲委派机制
为了保证安全

1.类加载器收到类加载请求
2.先看看这个类是否已经被加载过,将这个类加载请求委托给父加载器去完成,一直向上委托并且查看这个类是否已经被加载,直到根加载器
3.根加载器检查是否能加载当前这个类,如果能,就加载,如果不能,就抛出异常,通知子加载器进行加载
4.重复步骤3

在这里插入图片描述

5.沙箱安全机制
Java安全模型的核心是沙箱
5.1 什么是沙箱?
沙箱是限制程序运行的一种环境
沙箱机制就是将java代码限定在虚拟机JVM特定的运行环境之中,并且严格限制代码对本地系统资源的访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源的访问
系统资源包括(CPU,内存,文件系统,网络)
不同级别的沙箱对这些资源的访问的限制是不同的

组成沙箱的基本组件:
1.字节码校验器:确保java文件遵循java语言规范,这样可以帮助java程序实现内存保护。但并不是所有的类文件都会经过字节码校验器校验,比如核心类
2.类加载器
3.存储控制器
4.安全管理器

6.Native
Thread类中的start方法中就调用了本地方法栈
Thread类中有这么一段代码
public native void start0();

凡是带了native关键字的,就会进入本地方法栈,说明java的作用范围达不到了,会去调用底层C语言的库,当其进入本地方法栈后,会去调用本地方法接口(JNI)
JNI的作用是扩展java的使用,融合不同的编程语言为java所调用:最初是想融合C和C++
因为当时C++和C盛行,所以java当时认为自己想要生存下去,就必须要有调用C和C++的程序,所以他在内存中专门开辟了一块标记区域:Native Method Stack(本地方法栈),用来登记native方法,在执行引擎的执行的时候加载Native Libraies
但是现在从java想要调用其他语言写的程序的时候,一般不会用navite了,除非是想和硬件打交道,现在一般使用API接口,通过socket网络传输实现不同语言之间的互相调用

7.PC寄存器
程序计数器(Program Counter Register)
每个线程都有一个程序计数器,是线程所私有的。
本质其实是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,即将要执行的指令代码),用于执行引擎读取下一条指令。
程序计数器占用非常小的内存空间,几乎可以忽略不计

程序计数器的特点:

(1)程序计数器具有线程隔离性

(2)程序计数器占用的内存空间非常小,可以忽略不计

(3)程序计数器是java虚拟机规范中唯一一个没有规定任何OutofMemeryError的区域

(4)程序执行的时候,程序计数器是有值的,其记录的是程序正在执行的字节码的地址

(5)执行native本地方法时,程序计数器的值为空。原因是native方法是java通过JNI调用本地C/C++库来实现,非java字节码实现,所以无法统计

8.方法区,栈,堆

方法区:静态变量,常量,类信息(构造方法,接口定义),运行时的常量池存在于方法区中,但是,实例变量存在于堆内存中,和方法区无关

栈:先进后出
生命周期和线程同步,当线程结束的时候,栈内存也便释放了,对于栈来说,是不存在栈内存溢出的,同时也不存在垃圾回收机制

堆:
Heap: 一个堆内存的大小是可以调节的。
堆的内存中还会细分为
新生区:
老年区:
永久区:
在这里插入图片描述
垃圾回收主要是在伊甸园区和养老区
假设内存满了,OOM,堆内存不够了!
OOM:OutOfMemoryError

在JDK8以后,永久存储区改了个名字(元空间

9.新生区,老年区,永久区
新生区:一个类诞生,成长,甚至“死亡”
Eden
to :谁空谁是to
from

老年区:从新生区经过GC垃圾回收“生存”下来的,会进入老年区

永久区:
这个区域是常驻内存的,用来存放JDK自身携带的Class对象,Interface元数据,存储的是java运行时的一些环境和类信息,这个区域不存在垃圾回收!
关闭VM虚拟机的时候就会释放这个区域的内存

jdk1.6之前:永久代,常量池是在方法区中
jdk1.7: 永久代,但是慢慢的退化了,开始追求去永久代,常量池在堆中
jdk1.8之后:无永久代,常量池在元空间中

调整JVM堆内存
-Xms1m -Xmx1m -XX:+PrintGCDetails

-Xms:设置初始内在分配大小
-Xmx:设置最大分配内存
-XX+PrintGCDetails:打印GC垃圾回收信息
-XX+HeapDumpOnOutOfMemoryError:oom Dump

10.堆内存调优

当一个项目中报了OOM的错误,该如何排除~?
使用专业的内存快照分析工具进行dump排错 Profiler

-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError

11.GC
JVM在进行垃圾回收的时候,并不是对三个堆区进行统一回收,大部分时候是对新生代的回收
GC分两类:轻GC 重GC

GC的算法:标记清除法,标记压缩,复制算法,引用计数器,怎么用的?

新生代主要使用复制算法:
1.每次GC都会将Eden活的对象移到幸存者区中,一旦Eden去被GC后,就会是空的
2.幸存者区中会进行将一个区复制到另外一个区中,从而使空的那个幸存者区成为to,有数据的那个区为from

复制算法:
在这里插入图片描述

好处:没有内存的碎片
坏处:浪费了内存空间:多了一半空间永远是空to,假设对象是100%存活,就会容易出现OOM(极端情况)

所以复制算法的最佳使用场景:对象存活度较低的时候:即新生区

在这里插入图片描述

标记清除算法:
先将新生区的对象进行可达性分析,判断出垃圾对象和存活对象,对存活对象进行标记,然后对没有标记的对象进行清除

好处:不需要额外的空间
缺点:两次扫描,严重浪费时间,会产生内存碎片

标记压缩:
标记清楚后进行压缩,目的是消除内存碎片

GC算法总结:
内存效率(时间复杂度):复制算法 > 标记清楚算法 > 标记清除压缩算法
内存整齐度:复制算法 > 标记清除压缩算法 > 标记清楚算法
内存利用率:标记清除压缩算法 > 标记清楚算法 > 复制算法

思考:是否存在着一种算法,是最优的算法
答案:没有,没有最好的算法,只有最合适的算法 -----> 分代收集算法

年轻代:
特点:存活率低
算法:复制算法

老年代:
特点:存活率高,区域大
算法:标记清除算法(内存碎片不算多的时候 )+ 标记压缩算法

12.JMM

JMM:Java Memory Modle
java内存模型:
1.什么是JMM?
java线程内存模型

数据的原子操作:
八大规则:
read(读取):从主内存读取数据
load(载入):将主内存读取到的数据写入工作内存
use(使用):从工作内存读取数据进行计算
assign(赋值):将计算好的值重新赋值到工作内存中
store(存储·):将工作内存数据写入内存中
write(写入):将store过去的变量值赋值给主内存中的变量
lock(锁定):将主内存变量加锁,标识为线程独占状态
unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

JMM对这八种指令的使用,制定了如下规则:
不允许read和load、store 和write操作之一单独出现, 即不允许一个变量从主内存读取了但工作内存不接受,或者工作内存发起回写了但主内存不接受的情况出现。
不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化( load或assign)的变量,换句话说就是对一个变量实施use、store 操作之前,必须先执行assign和load操作。
一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次, 多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作以初始化变量的值。
如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock-一个被其他线程锁定的变量。
对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)。
对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)。

在这里插入图片描述

缓存一致性协议(MESI):
多个CPU从主内存读取同一个数据到各自的高速缓存,当其中的某个CPU修改了缓存里面的数据,该数据会马上同步到主内存,其他CPU通过总线嗅探机制(一种监听)来感知到数据的变化从而将自己缓存里的数据失效

volatile
Volatile是Java虚拟机提供轻量级同步机制。

1、保证可见性。

2、不保证原子性

3、禁止指令重排

volatile 缓存可见性实现原理:会在汇编语言层面加上lock前缀,会锁定这块内存区域的缓存,并回写到主内存中
lock指令的作用:
1.会将当前处理器缓存行的数据立即写入到系统内存中
2.这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效(MSI协议),从而使其他CPU立即重新read和load数据,从而使volatile保证了可见性
3.提供内存屏障,使lock前后指令不能重排序(CPU有时会对一些无关系的代码进行执行顺序重新排序,比如读取操作会耗费100纳秒,而计算操作会耗费0.6纳秒,如果代码的执行顺序是先读取后计算(计算的时候不需要用到读取的数据),那么CPU会为了提高效率而先进行计算操作,然后才进行耗时的读取操作)

三种JVM
Sun公司 HotSpot
BEA JRockit
IBM J9 VM

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值