JVM八股速查

1.JVM组成

JVM由那些部分组成,运行流程是什么?

JVM是Java程序的运行环境

组成部分:

类加载器:加载字节码文件到内存

运行时数据区:包括方法区,堆,栈,程序计数器,本地方法栈

执行引擎:执行字节码,优化代码

垃圾回收器:管理堆内存

运行流程:

加载字节码,准备运行环境,执行字节码,垃圾回收,程序结束

什么是程序计数器

用于记录每个线程正在执行的字节码指令的地址,用于保存字节码行号。当一个线程执行一段字节码到某个位置时,CUP使用权被另一个线程夺走,当前执行的地址会记录下来,当执行权回到当前线程时,会接着上次记录的位置继续执行

介绍Java堆

堆是一个线程共享的区域,主要用于保存对象实例,数组等,堆中内存不够会抛出OOM异常

堆区分为年轻代和老年代,年轻代被化为三部分,一个Eden区和两个S区,一个对象创建后会先到Eden区,如果对象被垃圾回收后还能存活就移动到S0或S1,再经过几次垃圾回收后还能存活则移动到老年代区中,老年代中存的是生命周期比较长的对象,

JDK1.7和1.8堆的区别

1.7中有一个永久代,存类信息,静态变量,常量,编译后代码,1.8移除了永久代,将数据存到本地内存中的元空间区,防止内存溢出

什么是虚拟机栈

每个线程运行时所需要的内存称为虚拟机栈,是一个先进后出结构,每个栈由多个栈帧组成,每个对应着每次方法调用时所占用的内存和数据,每个线程只能有一个活动栈帧,对应了当前正在执行的方法

垃圾回收是否设计栈内存

垃圾回收主要指堆内存,当栈帧弹栈以后,内存就会释放

栈内存分配越大越好吗

未必,默认的栈内存通常为1024k,栈帧过大会导致线程数减少

方法内的局部变量是否线程安全

如果方法内局部变量没有逃离方法的作用范围,那就是线程安全的,变量的创建和销毁都是在当前线程的虚拟机栈中完成的

如果局部变量引用了其他对象并逃离的方法的作用范围,那就要考虑线程安全

栈内存溢出情况

栈帧过多导致内存溢出,如递归调用

栈帧过大导致内存溢出

堆和栈的区别

栈内存一般用来存储局部变量和方法调用,堆用来存储Java对象和数组,堆会用垃圾回收,栈不会

栈内存时线程私有的,堆内存是线程共享的

异常错误不同,内存不足时,栈报StackOverFlow,堆报OutOfMemory

解释方法区

方法区主要存类的信息,运行时常量池,是各个线程共享的内存区域,在虚拟机启动时创建,虚拟机关闭时销毁

方法区在JDK 1.7时在堆区的永久代中,JDK1.8后取消了永久代,单独存在元空间中,避免了在堆区的OOM

解释一下运行时常量池

常量池可以看作一张表,虚拟机指令可以根据这张表找到要执行的类名,方法名,参数类型等信息

当类被加载时,常量池的信息就会放入运行时常量池,并把符号地址转为真实地址

直接内存

不属于JVM的内存结构,是虚拟机的系统内存,常见于NIO操作,用于数据缓冲区,直接内存相当于一块操作系统和Java代码都可以访问到的共享区域,比如我们在文件IO操作中,使用传统的BIO,需要调用操作系统的文件API,涉及到CPU用户态和内核态的切换,资源开销很大,引入直接内存之后,可以通过直接内存建立起系统内存和Java内存的交互传输

2.类加载器

什么是类加载器

类加载器的作用是将字节码文件加载到JVM中,从而使Java程序能够运行起来

类加载器有哪些:

启动类加载器,扩展类加载器,应用类加载器(用户自己编写的Java类),自定义类加载器

什么是双亲委派机制

加载一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则继续向上委托,如果委托上级都没有加载,则子加载器尝试加载该类

JVM为什么采用双亲委派机制

可以避免一个类被重复加载,当父类加载后则无需重复加载,保证唯一性

为了安全,保证类库API不会被修改

类装载的执行过程

加载:根据类的全名获取类的字节码文件,将其转换为方法区内的运行时数据结构

验证:对字节码进行校验,确保符合JVM规范

准备:为类的静态变量分配内存,设置默认初始值

静态变量是final修饰的基本类型或字符串常量,赋值在准备阶段完成

静态变量是final修饰的引用类型,复制也初始化阶段完成

解析:将符号引用转为直接引用,即将类,方法,字段等解析为内存地址

初始化:执行类的初始化代码,包括静态变量赋值和静态代码块的执行

使用:JVM开始从入口方法执行用户的程序代码,如调用静态类的成员信息,使用new关键字创建对象实例

卸载:当用户程序代码执行完毕后,JVM开始销毁创建的Class对象

3.垃圾回收

简述垃圾回收机制

在Java语言中,有自动的垃圾回收机制,开发者只需要关注内存的申请,内存的释放由系统自动识别完成,不同的对象引用会有不同的回收机制

对象什么时候可以被垃圾回收器回收

如果一个对象或多个对象没有任何引用指向它了,那么这个对象现在就是垃圾,就有可能被GC回收

垃圾定位方法

引用计数法:一个对象被引用一次,则在当前对象头上递增一次引用次数,如果引用次数为0,则代表该对象可回收,如果出现循环引用的画计数法就会失效

可达性分析算法:会存在一个根节点,其引用指向下一个节点,依次向下类推,直到所有节点遍历完毕

判断如果某对象和根对象无直接或间接引用则可以被垃圾回收

JVM垃圾回收算法

标记清除算法:分标记和清除两个阶段,根据可达性算法通过GCRoot得出垃圾并进行标记,对这些标记为可回收的内容进行垃圾回收,效率高,有磁盘碎片,内存不连续

标记整理算法:标记清除的过程一样,但是会将清理后存活的对象都向内存的一端移动,然后清理边界之外的垃圾,无内存碎片,对象需要移动,效率低

复制算法:将原有的空间一分为二,每次只用其中一块,垃圾回收时,将正在使用的对象复制到另一块内存空间中,然后将当前空间清空,交换两块内存的角色,完成垃圾回收,无碎片,内存使用率低

JVM的分代回收

堆被分成两份,新生代和老年代(比例为1:2),对于新生代内部又分Eden区,两个幸存区:From,to

新创建的对象首先被分到Eden区,当Eden区内存不足时,标记Eden区和From区的存活对象并将其复制到to区中,复制完成后,Eden和from中的内存被释放,经过一段时间Eden区内存又不足,标记Eden和to区存活的对象,将存活的对象复制到from区,当幸存区对象熬过几次回收就会晋升到老年代

MinorGC、 Mixed GC 、 FullGC的区别是什么

MinorGC发生在新生代的垃圾回收,暂停时间短

MixedGC发生在新生代和部分老年代区域的垃圾回收。G1收集器持有

FullGC发生在新生代和老年代的完整垃圾回收,暂停时间长,应尽量避免

JVM有哪些垃圾回收器

串行垃圾回收器:垃圾回收时,只有一个线程在工作,其他线程都要阻塞等待垃圾回收完成。Serial作用于新生代,采用复制算法,Serial作用于老年代,采用标记整理算法

并行垃圾回收器:多个线程完成垃圾回收工作,其他线程同样阻塞,Parallel New用于新生代,采用复制算法,Parallel Old作用老年代,采用标记整理算法

CMS(并发)垃圾回收器:针对老年代进行垃圾回收,不会造成线程阻塞,会追踪标记整个GCRoot,将其直接关联和间接关联的对象

G1垃圾回收器,作用于新生代和老年代

详细聊一下G1垃圾回收器

用于新生代和老年代的垃圾回收,在JDK9后JVM默认用的G1垃圾回收,采用的回收算法是复制算法,分为三个阶段

首先G1将内存划分为多个区域,每个区域都可以充当Elen区,幸存者区。jumongous等

新生代回收:

初始时,所有区域处于空闲状态,创建了一些对象,挑出一部分空闲区左Eden区存储这些对象,当Eden区需要垃圾回收时,挑一个空闲区域做幸存区,用复制算法复制存活对象,需要暂停用户线程,再往后,Eden区内存又不足了,将Eden区以及幸存区的存活对象复制到新的幸存区,将较老的对象晋升至老年代

并发标记:

当老年代占用内存超过阈值(默认45%)后,触发并发标记,无需暂停用户线程,并发标记后会冲标解决漏标问题(此时需要暂停线程),这些都完成后就知道了老年代有哪些存活对象,之后进入混合手机阶段,此时不会堆老年代区域进行回收,而是根据暂停时间目标优先回收价值高的区域

混合收集:

复制完成,内存释放,进入下一轮垃圾回收,如果对象非常大,会开出Jupmgous区存储巨型对象

强引用,弱引用,软引用,虚引用

强引用:只要所有的GCRoots能找到,就不会被回收

软引用:需要配合SoftReference使用,当垃圾多次回收,内存依然不够用时会回收软引用对象

弱引用:需要配合WeakReference使用,只要进行垃圾回收,就会把弱引用回收

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放内存

4.JVM实践

JVM调优参数设置

war包部署在tomcat中设置

jar包部署在启动参数中设置

设置堆空间:-XMS(初始) -XMX(最大)

最大大小为默认物理内存的四分之一,初始大小时物理内存的六十四分之一

堆太小,会导致频繁的年轻代和老年代和垃圾回收,产生stw,用户线程阻塞,太大,可能会导致FullGC,会扫描整个堆空间,暂停用户线程的时间长

虚拟机栈设置:-XSS 每个线程默认1M

新生代中Eden区和两个幸存区的大小比例:默认8:1:1 -XXSus**=9 设置比例

年轻代晋升老年代的阈值: 默认为15,范围0-15

设置垃圾回收期

JVM调优工具

命令:

jps:查看进程状态信息

jstack:查看进程内线程堆栈信息

jmap:查询堆栈信息

jhatL堆转存快照工具

jstat:JVM统计监测工具

可视化工具:jconsole:JVM内存,线程,类监控 VisualVM:监控线程,内存情况

Java内存泄漏排查思路

使用jmap查询堆栈信息,生成dump文件

通过VisualVM,加载dump文件分析堆栈信息定位到代码排查

CPU跑到百分之百,解决思路是啥

通过top命令,定位到占用CPU高的线程

ps -T -p 进程ID找到进程中占比较高的线程

通过jstask查询线程的堆栈信息去定位代码

相关推荐
liuyang-neu34 分钟前
力扣 155.最小栈
java·算法·leetcode
Musennn2 小时前
leetcode98.验证二叉搜索树:递归法中序遍历的递增性验证之道
java·数据结构·算法·leetcode
WLKQ2 小时前
【力扣】关于链表索引
java·leetcode·链表
小黄人软件3 小时前
OpenSSL 与 C++ 搭建一个支持 TLS 1.3 的服务器
服务器·开发语言·c++
xujinwei_gingko4 小时前
Spring boot基础
java·spring boot
啊阿狸不会拉杆4 小时前
《软件工程》第 14 章 - 持续集成
java·ci/cd·软件工程
武昌库里写JAVA4 小时前
Vue3编译器:静态提升原理
java·开发语言·spring boot·学习·课程设计
bing_1584 小时前
HttpServletRequest 对象包含了哪些信息?
java·spring·mvc
xzdangelliu4 小时前
POI模板生成EXCEL 64000 style in a .xlsx Workbook
java·excel·poi