JAVA面试题总结-JVM(194-208)

194.说一下 jvm 的主要组成部分?及其作用?

类加载器(ClassLoader)

运行时数据区(Runtime Data Area)

执行引擎(Execution Engine)

本地库接口(Native Interface)

作用:首先通过javac编译器把Java代码转换成字节码文件,类加载器、运行时数据区再把字节码文件加载到JVM内存中,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎,将字节码文件解释成底层操作系统指令,再交给CPU去执行,而这个过程需要调用本地库接口来实现整个程序的功能。

195.说一下 jvm 运行时数据区?

JVM内存区域最粗略的划分可以分为堆、栈和方法区,当然,按照虚拟机规范,有以下几个组成部分:

程序计数器(Program Counter Register):也被称为PC寄存器,是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

Java虚拟机栈(Java Virtual Machine Stack)线程私有,他的生命周期与线程相同,Java虚拟机栈描述的是Java方法执行的线程内存模型:方法执行时,JVM会同步创建一个栈帧,用来存储局部变量表、操作数栈、动态连接等。

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,区别是虚拟机栈执行Java方法,而本地方法栈执行本地方法。

Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。堆是线程共享的区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,Java中几乎所有的对象实例都在这里分配内存。

方法区:方法区时比较特别的一块区域,和堆类似,是线程共享的,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

其中程序计数器和栈内存是线程私有的,堆和方法区是线程共享的。

196.说一下堆栈的区别?

堆占用的内存空间远大于栈。

堆是线程共享的,栈是线程私有的。

堆用来存储对象实例,栈用来执行程序的。

197.队列和栈是什么?有什么区别?

队列(Queue):是只能在表的一端插入另一端删除的线性表。

栈(Stack):是只能在一端进行插入和删除的线性表。

队列分为入队和出队,栈分为进栈和出栈。

队列在队尾入队,队头出队,栈的进栈和出栈都是在栈顶,无法堆栈底进行操作。

队列是先进先出规则,栈是先进后出规则。

198.什么是双亲委派模型?

想要知道双亲委派机制,就要先了解类加载器。

有四种类加载器,分别为:

启动类加载器:加载JAVA_HOME\lib 下的核心类库。

拓展类加载器:加载JAVA_HOME\lib\ext 下的拓展库。

应用类加载器:加载ClassPath下指定类库,一般情况下没有自定义类加载器,默认就是应用类加载器。

自定义类加载器:通过继承 java.lang.ClassLoader 类的方式自行实现的类加载器。

双亲委派机制:如果一个类加载器收到了类加载的请求,它首先不会尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一个类加载器都是如此,直到把请求传递给最顶层的启动类加载器中,只有当父类加载器反馈自己无法加载,子类加载器才会尝试去自己加载。

199.说一下类加载的执行过程?

类加载执行过程包括加载验证准备解析初始化

加载过程,JVM要做三件事:

获取类的二进制字节流:通过一个类的全限定名来获取定义此类的二进制字节流。

结构化静态存储结构:将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

在内存中生成Class对象:在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

通俗来讲就是,通过类的全限定名来获取二进制字节流,将这个二进制字节流的静态存储结构转换为方法区的数据结构,在内存中创建一个Class对象,作为访问方法区的入口。

验证:验证加载的class文件的正确性。

准备:给类中的静态变量分配内存空间。

解析:JVM将常量池中的符号引用替换成直接引用的过程。

初始化:对静态变量和静态代码块进行初始化操作。

200.怎么判断对象是否可以被回收?

一般有两种方法来判断:

引用计数算法:通过判断对象的引用数量来决定对象是否可以被回收,每个对象实例都有一个引用计数器,被引用+1,完成引用-1,任何引用计数器为0的对象实例都可以被当作垃圾回收。

优点:执行效率高,程序执行受影响小。

缺点:无法检测出循环引用的情况,导致内存泄漏。

可达性分析算法:目前Java虚拟机的主流垃圾回收器采用的就是可达性分析算法,基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,根据引用关系进行向下搜索,搜索过程所走的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,也就是GC Roots到这个对象不可达时,则认为此对象可以被回收。

201.java 中都有哪些引用类型?

强引用:强引用是最传统的引用,是指程序代码中存在的引用赋值,无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用:用SoftReference实现,用来描述一些还有用,但是非必须的对象,被软引用的对象,会在JVM内存溢出前被垃圾收集器二次回收。

弱引用:用WeakReference实现,用来描述那些非必须对象,当垃圾收集器开始工作,弱引用对象会被收集器回收。

虚引用:也称幽灵引用,用PhantomReference实现,是最弱的引用关系,唯一目的是在这个对象被垃圾收集器回收时收到一个系统通知。

202.说一下 jvm 有哪些垃圾回收算法?

标记清除算法:最基础的回收算法。

主要存在两个缺点:

执行效率不稳定,如果Java堆中包含大量对象并且大部分都是需要回收的,这时就必须进行大量的标记和清除动作,导致标记和清除随着对象的增多而效率降低。

内存空间的碎片化问题,标记清除之后会产生大量不连续的内存碎片,如果内存空间碎片太多可能会导致需要分配大对象时找不到足够的连续内存空间,此时不得不提前触发垃圾回收操作。

标记复制算法:解决了标记清除面对大量可回收对象时执行效率低的问题。

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了 ,就会将还存活着的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。

明显缺点:一部分空间没有使用,存在空间浪费

新生代垃圾收集器主要采用这种算法,因为新生代存活对象比较少,每次复制的只是少量的存活对象。

标记整理算法:标记过程和标记清除算法一样,但后续步骤不是直接将可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

主要用于老年代,移动存活对象是一个非常负重的操作,这种操作需要Stop The World才能进行。

203.说一下 jvm 有哪些垃圾回收器?

Serial:最基础的、历史最悠久的单线程垃圾收集器。使用标记复制算法。

Serial Old:Serial的老年版本,也是单线程的,使用标记整理算法。

ParNew:Serial的多线程版本,使用标记复制算法。

Parallel:吞吐量优先的多线程垃圾收集器,使用标记复制算法。

Parallel Old:Parallel的老年版本,支持多线程,使用标记整理算法

CMS(Concurrent Mark Sweep):是一种以获取最短回收停顿时间为目标的收集器,老年代的收集器,非常实用B/S架构,采用标记清除算法。

G1(Garbage First):是垃圾收集器的颠覆性产物,兼顾吞吐量和停顿时间的垃圾收集器,是JDK9以后的默认GC选项。

204.详细介绍一下 CMS 垃圾回收器和G1垃圾回收器?

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的垃圾收集器,从名字就能知道使用的标记清除算法,但是它比一般的标记清除算法复杂,分为四个阶段:

1、初始标记:标记GC Roots能关联到的对象,会“Stop The World”。

2、并发标记:从GC Roots关联的对象开始遍历对象图,可以和用户线程并发执行。

3、重新标记:标记并发标记阶段产生的对象,会“Stop The World”。

4、并发清除:清除对象,可以和用户线程并发执行。

CMS会产生浮动垃圾,就是在并发清除阶段用户线程还在运行,此时会有新的垃圾产生,但是由于是在标记之后,CMS无法清除这部分垃圾,只能等到下一次GC才能清理。

G1(Garbage First)收集器是垃圾收集器一个颠覆性的产物,它开创了局部收集的设计思路和基于Region的内存布局形式。G1收集器整体上使用标记整理算法,局部使用标记复制算法,它可以避免CMS收集器产生的内存碎片问题,通过看源码可以发现G1默认会把堆内存分成2048份,也就是2048个Region,每一个Region都会扮演不同的角色,G1收集器的运行过程大致可以分为四步:

1、初始标记:标记从GC Roots能关联到的对象,会“Stop The World”。

2、并发标记:对堆中的对象进行可达性分析,遍历对象图,找出可回收对象,可以和用户线程并发执行。

3、最终标记:标记在并发标记阶段产生的对象,会”Stop The World“。

4、筛选回收:指定回收计划,选择多个Region构成回收集,清除对象,会”Stop The World“。

205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

新生代垃圾回收器:Serial、ParNew、Parallel

老年代垃圾回收器:Serial Old、Parallel Old、CMS

整堆回收器:G1

新生代垃圾收集器一般采用的是标记复制算法,因为新生代中保留的对象少,所以用标记复制算法效率高,缺点是内存利用率低。

老年代垃圾收集器一般采用标记整理算法,因为老年代中需要垃圾回收的对象少,用标记整理效率高,其中CMS用的是标记清除算法。

206.简述分代垃圾回收器是怎么工作的?

分代收集器分为新生代和老年代,新生代默认空间占比为1/3,老年代默认空间占比为2/3。

新生代使用的是标记复制算法,新生代有三个分区:Eden、To Survivor、From Survivor,他们的占比为8:1:1,执行流程:

把Eden和From Survivor中存活的对象放入To Survivor区中;

清空Eden和From Survivor分区;

From Survivor和To Survivor分区互换,From Survivor变成To Survivor,To Survivor变成From Survivor。

每次From Survivor到To Survivor移动存活的对象,都会使对象年龄+1,当对象年龄到达默认值15时,进入老年代,大对象也会直接进入老年代。

老年代一般使用标记整理算法,通过以上的循环反复构成分代垃圾回收机制。

207.说一下 jvm 调优的工具?

以下是JDK自带的可视化性能监控和故障处理工具:

JConsole、VisualVM、Java Mission Control

除此之外还有一些第三方的工具:

MAT:Java堆内存分析工具

GChisto:GC日志分析工具

GCViewer:GC日志分析工具

JProfiler:商用的性能分析利器

arthas:阿里开源诊断工具

async-profiler:Java应用性能分析工具,开源、火焰图、跨平台。

208.常用的 jvm 调优的参数都有哪些?

堆配置:

-Xms:初始堆大小

-Xmx:最大堆大小

-XX:NewSize=n:设置年轻代大小

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为 3 表示年轻代和年老代比值为 1:3,年轻代占整个年轻代年老代和的 1/4

-XX:SurvivorRatio=n:年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。如 3 表示 Eden: 3 Survivor:2,一个 Survivor 区占整个年轻代的 1/5

-XX:MaxPermSize=n:设置持久代大小

收集器设置:

-XX:+UseSerialGC:设置串行收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParalledlOldGC:设置并行年老代收集器

-XX:+UseConcMarkSweepGC:设置并发收集器

并行收集器设置

-XX:ParallelGCThreads=n:设置并行收集器收集时使用的 CPU 数。并行收集线程数

-XX:MaxGCPauseMillis=n:设置并行收集最大的暂停时间(如果到这个时间了,垃圾回收器依然没有回收完,也会停止回收)

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为:1/(1+n)

-XX:+CMSIncrementalMode:设置为增量模式。适用于单 CPU 情况

-XX:ParallelGCThreads=n:设置并发收集器年轻代手机方式为并行收集时,使用的 CPU 数。并行收集线程数

打印 GC 回收的过程日志信息

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值