1.JVM运行时结构,各个区存放的数据
按线程可见性来分可以分为下面这两类
线程共享
堆区:堆用来存放数组和对象,内部又分为了新生代和老年代,他们的比例默认为1:2,新生代又分为了一个Eden和两个Survivor,默认比例为8:1:1方法区:用来存放类的方法、属性、字符串常量池、静态变量等。在1.7的时侯,字符串常量和静态变量都放在永久代
,在1.8的时候移除了永久代,永久代在启动之前必须制定初始大小,FGC还不会对它进行垃圾回收,新加入了元空间(MetaSpace)
,将原本存放在永久代的数据全部移到了元空间,元空间启动不需要制定大小,默认最大大小就是物理内存,FGC会对它进行垃圾回收。
线程独占
虚拟机栈:存放一个个栈帧,每个方法调用都会生成一个栈帧,用来存放局部变量表、操作数栈、动态链接、以及方法的出口信息本地方法区:存放线程可能会使用到的本地方法PC寄存器:记录当前线程执行的指令
2.类加载机制
类加载一共分为以下三个步骤
1.加载:把字节码文件load到内存中的方法区,生成一个Class对象,这个Class指向了这块内存
2.连接:连接又分为了三个步骤
验证:验证字节码文件是否符合JVM规范准备:给静态变量赋默认值解析:将类属性、方法的符号引用解析为直接引用,常量池的符号引用解析为指针
3.初始化:执行静态代码块,给静态变量赋值
3.什么是双亲委派机制,为什么要使用双亲委派
JVM建议类加载时遵循的一种规范,每个类加载器都有自己的父类加载器,当一个加载器接收到一个加载任务,会先去自己的缓存中找,没找到就委派给父类的加载器,直到委派给顶层的加载器,缓存中还是没找到后,就开始尝试进行加载,加载失败就委托给自己的下一层类加载器去加载,如果到了底层的加载器还是没加载成功,就报ClassNotFoudException的异常。
使用双亲委派的原因:主要是为了安全,确保类的唯一性,防止用户自定义类覆盖掉了JDK核心的类。
4.什么是cpu的乱序问题,操作系统层面如何保证不乱序执行
cpu为了提高指令执行的效率,会在一条指令执行过程中,同时区执行另一条指令,前提是两条指令没有依赖关系
确保指令的顺序执行:
cpu的内存屏障lock的汇编指令
5.对象的创建过程具体是怎样的
加载–>连接(验证、准备、解析)–>初始化–>申请内存空间–>成员变量赋默认值–>调用构造方法(成员变量赋初始值)
6.对象在内存中的存储布局
对象
对象头:8个字节ClassPoint(指针):开了压缩(-XX:+UseCompressedClassPointers默认开启)是4个字节 不开8个字节实例数据:引用类型开了压缩是4个字节 不开是8个字节Padding对齐:为了对齐8的倍数
数组:数组就比普通对象多了个长度,4个字节
7.对象头中具体包含了那些信息
对象头占了8个字节,具体包含的信息如下(64位):
锁信息:用了3位来表示,用了1位来表示是否偏向锁,另外两位用来标记锁状态GC的分代年龄:用了4位来表示,也就是说对象的分代年龄最大就是15对象的HashCode值:占了31位
8.什么是垃圾
没有任何引用指向的对象就可以称为垃圾对象
9.寻找垃圾的算法
引用计数器法:这个算法会有循环引用的问题根可达分析算法10.GC Roots对象包括哪些
线程栈对象、静态变量、常量池、JNI指针(本地方法对象)
11.垃圾的清除算法,说说它们的优缺点
标记清除
优点:算法简单,存活对象较多的情况下,效率比较高缺点:需要扫描两遍,效率比较低,容易产生内存碎片
拷贝算法
优点:不会产生内存碎片,缺点:内存一分为二,浪费内存空间,移动复制对象需要调整对象引用
标记压缩
优点:不会内存减半,也不会产生内存碎片,方便对象分配缺点:两次扫描,移动对象需要调整对象引用,效率较低
12.什么是栈上分配、线程本地分配TLAB
栈上分配:用来分配那些不会逃逸出方法的对象,使用普通类型来代替对象
TLAB(Thread Local Allocation Buffer):线程创建时,申请的一块内存,默认占整个Eden的1%(可以通过参数配置),小且无逃逸对象分配时会先尝试线程本地分配
优点:多线程情况不用竞争Eden,就可以申请内存空间,提高了效率
13.什么是动态年龄
触发YGC时,Eden和其中一个Survivor存活的对象,内存占比大于另一个Survivor区的50%时,一些年龄比较大的对象会被移到老年代
14.什么是分配担保
触发YGC时,如果有对象进来,内存不足,就会将对象直接分配到老年代,这个就是担保机制
15.常见的垃圾回收器有哪一些
Serial:分代模型,单线程最快的垃圾回收器,搭配Serial Old使用
Serial Old
Parallel Scavenge:分代模型,并发清理,搭配Parallel Old使用
Parallel Old
ParNew:分代模型,为了配合CMS使用而衍生出来的,其实就是升级版的Parallel Scavenge
CMS:分代模型,开启了并行回收的先河,但是毛病比较多,会产生内存碎片、浮动垃圾
G1:逻辑分代,物理不分,JDK1.7加入的,将内存分为了一个个分区,每个分区都可以表示Eden、Survivor、Old、Humongous
ZGC:采用颜色指针来找垃圾对象
Shenandoah:采用颜色指针来找垃圾对象
Epsilon:调试用的不是很重要
16.CMS的回收步骤,它有什么问题,怎么避免这些问题
初始标记:找到GCRoots对象,时间比较短(STW)并发标记:耗时最长的一步,采用并发执行重新标记:找到漏标的对象,重新找那些垃圾对象(STW)并发清除:会产生浮动垃圾,这些垃圾会在下一次GC的时候清理掉问题:会有内存碎片和浮动垃圾,降低触发CMS的阈值,
17.Hospot参数分类有哪些
-:标准参数,所有的Hotspot都支持-X:非标参数,特定版本的Hotspot支持-XX:不稳定参数,下个版本可能会删除18.什么是性能调优
根据业务需求对JVM进行规划和预调优优化JVM的运行环境(慢、卡顿)解决JVM运行过程中出现的各种问题(OOM)19.说一下调优中的吞吐量和响应时间
吞吐量 = 用户代码执行时间/(用户代码执行时间 + GC回收时间)
响应时间:STW越短,响应时间越好
20.说一下调优的具体步骤
1.首先通过top或Jps命令找到程序的pid
2.通过jstack查看进程中,线程的具体运行情况,注意那些线程状态为WAITTING和BLOCKED的线程
3.通过jmap -histo命令找出程序中哪些对象占用内存比较大
4.通过上面这些信息找到问题,然后做出相应的解决
21.说一下三色标记算法
垃圾标记的一种算法,将对象分为三种颜色
黑色:自身和属性都被标记的对象灰色:自身被标记对象,但是属性还没有标记白色:未被标记的对象
22.说一下G1的内部结构和特点
G1是JDK1.7的时候加入的,它严格意义来讲不属于分代模型,G1逻辑分代,物理不分,它把内存分为了一个个分区,每个分区都可以代表Eden、Survivor、Old、Humongous。
特点:
压缩空闲空间不会延长暂停时间
更易预测GC的停顿时间
适用并发不是特别高的引用场景
23.G1会触发那些GC
YGC、Mixed GC、FGC
24.什么是CSet(Collect Set)、Card Table、以及什么是Rset(Remember Table)
CSet:一组可被回收分区的集合,GC过程中,存活的对象会被移到其它的分区,CSet里面的分区可能来自 Eden、Surviror、OldCard Table:由于YGC需要扫描整个Old区,JVM设计了Card Table,如果有对象指向Y区,那么将这个Card Table设置为Dirty,下次只需要扫描这些Dirt Card就行RSet:记录了其它分区到本分区的对象引用,它的价值在于,垃圾回收器不需要扫描整个堆,找到对象引用,只需要遍历RSet即可25.G1发生FGC是使用哪个垃圾回收器来回收的,怎么优化G1的FGC
G1发生FGC的话,是采用单线程的Serial来回收的,所以尽量不要让G1发生FGC,我们可以扩大内存、增加CPU的响应速度、降低Mixed GC的触发阈值(默认45%)来对它进行优化
26.什么是Mixed GC
它类似于CMS,回收步骤也有四个分为为:
初始标记:找到GCRoots对象(STW)并发标记:耗时最长的一个步骤最终标记:找到漏标对象,扫描并找到垃圾对象(时间特别短,也是STW)筛选回收:STW(并行)
27.什么是漏标,什么情况下会发生漏标,解决方案有哪些
一个存活的对象,由于没有被GC扫描到,导致被回收掉
发生漏标的两种情况
在remark阶段,黑色指向白色,没有对黑色对象重新扫描时,白色对象会被当成垃圾回收掉。在并发标记阶段,灰色指向白色的引用失效,此时白色对象应该被回收掉。
解决方案
增量更新:关注引用增加,如果有黑色指向白色,将黑色改为灰色,下一次扫描它的属性(CMS使用的就是这种方式)STAB:关注引用删除,如果灰色指向白色引用失效,将白色引用推到GC的堆栈,确保能够被扫描到(G1使用的就是这种方式)
28.为什么G1处理漏标要使用STAB而不使用增量更新
由于G1中RSet的存在,不需要遍历整个堆区去找白色对象的引用,效率比较高
29.Arthas如何来排查系统问题
通过dashboard命令观察线程和堆得使用情况通过Thread pid命令查看线程内部的运行情况通过jvm命令查看虚拟机的详细信息如果觉得《JVM面试题附带答案》对你有帮助,请点赞、收藏,并留下你的观点哦!