jvm面试相关(不喜勿喷)

八种基本数据类型

  • 字符类型

  • char

  • 数字类型

  • int

  • float

  • long

  • short

  • double

  • byte

  • 布尔类型

  • boolean

jvm内存模型(运行时数据区)

执行逻辑:类通过类加载器进入到方法区|元空间,当然类的相关信息或者指令都放到了元空间,当执行到main方法的时候或者开启线程的时候会创建一个线程栈|栈空间,不管是new也好还是执行其他指令也好,都交给程序计数器,由程序计数器通知给执行引擎,执行引擎去元空间或者方法区去知道自己要执行那条指令,执行这条指令产生的数据如果是new出来的对象就放到堆空间,如果是一些局部变量就存放到线程栈,如果是native方法修饰的变量就放到本地方法栈。不过现在执行都是在栈桢内部实现的,我们创建对象后,该对象的引用地址会存放到局部变量表、又或者是进行赋值的数据都会存放到局部变量表,通过压栈的方式将数据传递给操作数栈,操作数栈通过动态链接将代码的符号引用转化为元空间内当前类的一些符号进行操作,期间也会频繁和程序计数器打交道,最后通过方法出口执行完毕,就回到主方法的位置。

1. 线程栈 - 线程私有

栈空间,也叫线程栈,因为这块是线程独享的空间。存放的数据是方法的栈桢

JVM的每一个线程对应一个线程栈,一个线程的每个方法会分配一块栈内存空间。栈帧中包含:局部变量表、操作数栈、动态链接和方法出口。

  • 局部变量表:存储八种基本数据类型(int、float、byte等),如果是引用数据类型,则存储的是其在堆中的内存地址,也就是指向对象的一个指针。

  • 操作数栈:操作数运算时一块临时的空间来存放操作数,操作的时候局部变量的数据不会删除。

  • 动态链接:将代码的符号引用转换为在方法区(运行时常量池)中的直接引用。

  • 方法出口:存储了栈帧中的方法执完之后回到上一层方法的位置。

2. 本地方法区(本地方法栈) - 线程私有

运行本地方法的空间,也就是native本地方法运行时的一块空间,执行本地方法native会产生一些数据,这些数据就是存放到该区域

3. 程序计数器 - 线程私有

类加载后,会有很多代码,这些代码其实就是一些指令,这些指令其实都是有序号的,通过对类进行反编译后,会看到一些指令,这些指令都是有序号的,这些序号就是通过程序计数器来告诉执行引擎,执行引擎通过cpu来执行具体的指令,把这些代码放到cpu寄存器里面,然后通过运算器进行运算执行。执行引擎如何知道我们要执行那行代码呢?其实就是通过程序计数器,它会把执行指令编码发给执行引擎,那么就知道怎么怎么顺序执行了 (通过javap -v 加.class文件就可以查看字节码文件)

4. 方法区(元空间)- 线程共享

当一个类被加载到运行时数据区时,会首先加载到元空间

主要包括:类信息(比如说有哪些方法,父类是谁,父类方法是谁)、常量、静态变量、运行时常量池,操作的是直接内存。

5. 堆 - 线程共享

存放的是new出来的类对象,但是并非new出来的对象都在堆空间,在一个方法内,如果对象未被出口,方法结束后就销毁,此时的对象是在栈空间。它在 JVM 启动的时候被创建。对象所占的堆内存是由自动内存管理系统管理也就是垃圾收集器回收。

  • Survivor区 区域划分见4.jpg

  • 年轻代 1:3

  • eden 8:10 存储对象,如果满了后,通过GCRoots可达性算法找到垃圾,通过minor GC收集回收垃圾,其他存活对象通过复制算法复制到s0或者s1

  • s0 1:10 将s1移动到s0或者将eden移动到s0

  • s1 1:10 将s0移动到s1或者将eden移动到s1

  • 老年代 2:3 如果说新生代的年龄达到设置的年龄后会移动到老年代 当老年代满了后发生fullGC

  • minor GC也叫youngGc:会出现STW,eden 或者s0 或者s1正在进行垃圾回收的时候会出现卡顿,stw时间比较短 可忽略不计

  • Major GC:老年代的GC

  • GC root:就是虚拟机栈的本地变量、静态变量、本地方法栈的变量等

  • full GC:当老年代满了以后,fullGc会去回收老年代和年轻代的垃圾,此时发生的stw会远远长于minor GC 反正数十倍大小 等同于majorGC+minorGC
    像eden对象移动到s1的时候采用的垃圾回收算法其实是复制算法

6.JVM中对象及常量、局部变量、全局变量的存储位置

1. 局部变量

基本数据类型:变量名和变量值存储在方法栈中。

引用数据类型:变量值存储在方法栈中(存储的是堆中对象的内存地址),所指向的对象是存储在堆内存中(如new出来的对象)。

2. 全局变量|普通成员变量

基本数据类型:变量名和变量值存储在堆内存中。

引用数据类型:变量名存储的是所引用对象的内存地址,变量名和变量值存储在堆内存中

3.常量

final static修饰的常量在方法区中的常量池

static修饰的变量在方法区中

对象逃逸分析

1.7之前,对象创建都是在堆空间进行,但是有个问题,方法中未被外部访问的对象

test1,这里面对象频繁在堆空间创建,方法出口后,需要对对象进行gc,浪费了性能

1.7以后,就会进行一次对象逃逸分析,于是没有被外部方法访问的对象就直接在栈上创建,随着方法的出栈而销毁,无须GC。

在栈上分配内存的时候,会把聚合量替换为标量,来减少栈空间的开销,也是为了防止栈 上没有足够连续的空间存放对象

  • 标量:Java中的八种基本数据类型

  • 聚合量:引用的数据类型

也就是说,在栈上分配内存的时候,会把引用数据类型如对象之类的拆分为八种基本数据类型。

逃逸的条件:

  • 能否逃出当前方法,也就是当前方法是否将对象返回出去

是不是所有的对象和数组都会在堆内存分配空间(仅限1.7以后jdk环境)

不一定,随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。但是这也并不是绝对的。在开启逃逸分析之后,也并不是所有对象都没有在堆上分配

如何判断一个对象是否是垃圾|垃圾定位

引用算法:相互引用

有个缺点:就会产生过多的垃圾,A引用B,B引用A,

可达性算法?

eden区满后,会触发GC root,将GC roots作为起点,从该节点开始向下搜索引用对象,找到的对象都标记为非垃圾对象,其余未标记到的对象就都标记为垃圾对象。

可以作为GC Roots的对象有以下几种:

  • 1.虚拟机栈中引用的对象

  • 2.方法区类静态属性引用的变量

  • 4.本地方法栈JNI引用的对象

垃圾回收算法?

1.标记清除算法(存活对象标记,未标记,循环挪到空间链表后进行删除,涉及移动,所以慢,适用存活对象多的)

标记清除算法是先找到内存里的存活对象并对其进行标记,然后统一把未标记的对象统一的清理。

缺点:位置不连续 产生空间碎片

特点: 简单、收集速度快,但会有空间碎片,空间碎片会导致后面的GC频率增加。

适合场景:存活对象多,需要回收的少,适用于老年代的垃圾回收,因为老年代(老年代存活对象多,垃圾对象比较少)一般存活对象会比回收对象要多。

废话:会扫描所有对象,将那些挂未被标记的进行一次遍历,这期间会把未被标记的对象加入到空闲的空间链表中作为空闲的块,而且这个阶段对堆对象越多的耗费的时间久越长,也就证明了未被标记的对象越多,耗费的时间越长,性能就会越差

2.复制算法(存活对象复制到另一块内存,然后清空当前内存,存活少的情况,存活越少复制就越快,所以适用新生代)

按照内存容量将内存划分为相等大小的两块内存区域。每次使用时只使用其中的一块,当这一块内存满后将存活的对象复制到另一块内存区域上去,将剩余的已使用的内存回收。

这种算法实现简单,内存效率高,不易产生内存碎片,但是内存使用率被压缩到了以前的一半。当存活对象增多时,复制算法的效率会大大降低。

缺点:没有碎片但是浪费一半的空间

特点:收集速度快,可以避免空间碎片,但是有空间浪费,存活对象较多的情况下复制对象的过程等会非常耗时,而且需要担保机制。

适合场景: 只有少量对象存活的场景,这也正是新生代对象的特点,所以一般新生代(存活少,绝大多数挂掉的对象被回收)的垃圾回收器基本都会选择标记复制法。

废话:从概念知道,将存活的对象都移到另一块内存,剩下的对象都被回收掉,也就是存活对象少的话,移动速度就快,这块就比较使用eden|s0|s1区了

3.标记整理算法

结合以上两个算法,为了避免缺陷而提出,标记阶段和标记清除算法相同,但标记后不是清理对象,而是将存活的对向移至内存的一端,然后清除未标记的另外一块内存的对象,将可回收的空间回收后将存活的和未使用的空间全部整理

缺点:没有碎片,效率偏低(主要是要对存活和未存活的对象进行挪动,虽然不会造成空间浪费,但是牺牲了性能)

特点: 相对于标记复制法不会浪费内存空间,相对标记清除法则可以避免空间碎片,但是速度比其他两个算法慢(主要是又要标记,又要将没被标记挪到其他内存块,所以慢)。

适合场景: 内存吃紧,又要避免空间碎片的场景,老年代想要避免空间碎片问题的话通常会使用标记整理法。

常用垃圾收集器?

1.8及其之前的版本

年轻代 (涉及到复制算法的都为年轻代)

  • serial(老版本前收集器)(适用于单核服务器|小型应用): 串行收集的,采用复制算法,不仅只用一条线程收集,而且在收集的同时所有用户线程必须停止(stop the world)

  • ParNew(适用于多核服务器):searial的多线程版本,并行收集的,与searial无区别,单核情况下性能会比searial低,所以默认开启的收集线程和cpu数量一致,收集是会开启多个线程进行收集,同时所有用户线程进入等待状态 和cms老年代收集器搭配使用

  • parallelScavenge(1.8默认收集器)(适用于多核服务器,并且注重吞吐量,高效利用cpu资源): 并行的收集器,和parnew最大的区别就是parnew的目标尽可能缩短用户线程等待时间,parallelScavenge的话是将整块达到一个可以控制的吞吐量(吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间))

老年代 (复制算法除外都在老年代)

  • Serial old(老版本前收集器):是searial老年代版本,同样也是一个单线程的收集器,采用标记整理算法,与searial搭配使用

  • CMS :采用标记清除算法,以最短回收停顿时间为目标,整个过程耗时最长的并发标记、并发清除都是和用户线程一起工作的,总的来说就是并行执行。整个过程会造成有些垃圾无法处理,主要是在清理阶段的时候,用户线程会产生垃圾,而且该收集器还不能和其他收集器一样等老年代填满了才收

  • 初始标记:标记活跃对象,速度快

  • 并发标记:标记全部垃圾对象,耗时长

  • 重新标记:修改并发标记记录,让用户线程继续运行时,导致变化的对象的标记记录,耗时短

  • 并发清除:采用标记-清除算法清除垃圾对象,耗时较长

  • Parallel Old(1.8默认收集器):和parallel Scavenge一起使用,是这个的老年代版本,多线程的收集器,采用标记整理算法,和parallel scavenge使用可以有效发挥多核cpu的计算能力

G1 1.9默认的垃圾收集器(适用新生代和老年代)

采用的标记整理算法,不会产生内存碎片,但是会产生stop the world

  • 初始标记

  • 使用一个垃圾回收线程进行初始标记,此刻其他线程暂停,主要标记的是什么样的对象垃圾:标记GCroot能够直接关联到的对象

  • 并发标记

  • 从GCRoot触发可达性分析存活对象,和用户代码进行并行执行,

  • 重新标记

  • 重新标记,也是记录用户线程在运行过程中,产生的垃圾对象,该过程是多个minorGCthread执行的

  • 初始清除

  • 回收掉新生代区域垃圾对象

  • 并发清除

  • 清理掉老年代区域产生的垃圾对象

G1和cms的区别

  • G1适用于新生代和老年代,cms仅适用于老年代

  • CMS主要采用的是分代模型如新生代老年代,G1是采用分区模型,将堆分为2048个区,每个区域都可以作为新生代和老年代适用并且大小可以根据期望暂停的时间动态进行调整

  • G1主要流程为初始标记、并发标记、重新标记、初始清除、并发清除,CMS主要是初始标记、并发标记、再次标记、并发清除,cms采用了标记清除算法,会产生很多内存碎片,G1采用的标记整理算法,不会产生内存碎片,但是会出现stw,通过初始清理、并发清理对垃圾进行回收,最后重新分配区域比例

  • G1堆特别大的应用,cms适用于堆不大不小的

评价垃圾回收算法好坏的标准?

  • 吞吐量(用户运行代码时间/(用户运行代码时间+垃圾回收时间)) 越高越好

  • 停顿时间 越低越好

题外话,一些更厉害的收集器

  • ZGC 11默认垃圾收集器

  • shenandoah G1增强版 redhat开发

  • Epsilon 测试 不会给你真正的垃圾手机

垃圾收集器组合的优缺点

  • 串行收集器->serial和serial old

  • 只能有一个垃圾回收线程执行,用户线程暂停。适用于内存比较小的嵌入式设备

  • 并行收集器(吞吐量优先)->parallel scavenge、parallel old

  • 多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态,适用于科学计算,后台处理等着交互场景

  • 并发收集器(停顿时间有限)->CMS,G1

  • 用户线程和垃圾线程同时执行,垃圾收集线程执行的时候不会停顿用户线程运行。适用于相对时间有要求的场景,比如web

如何搞定jvm内存溢出?

  • 发生的原因?
    老年代满了但是Gcroots通过可达性算法分析一直回收不掉 此刻就会发生oom

  • 解决方案:加大jvm内存 加大年轻代的比例,提高对象的分代年龄,提高s区比例,避免代码内存泄漏

  • top 查看所有进程,top -hp 查看当前进程下的所有线程,jstack -grep

  • 观察 如果是业务逻辑 检查业务逻辑 ,如果是系统线程就查询为什么频繁GC 最后再加上GC日志的处理

  • 减少GC频率---G1

  • 适当增加对内存的空间

  • 合理设置G1垃圾收集器的停顿次数

  • 垃圾回收的临界线

  • -xx:InitalingHeapOccupanPercent = 50

  • 增加垃圾回收线程数量

  • -xx:ConcGCThreads = 10

内存泄漏:内存泄漏其实就是空间无法回收最后就会造成oom outOfMemoryError

jvm调优 调的是什么

  • oom

  • jvm内存预分配(新生代和老年代,eden区和s0 s1区)

  • 使用的是那一套垃圾回收处理机制

  • 频繁stw情况

jvm参数

堆栈内存相关

  • -Xms 设置初始堆的大小

  • -Xmx 设置最大堆的大小

  • -Xmn 设置年轻代大小,相当于同时配置-XX:NewSize和-XX:MaxNewSize为一样的值

  • -Xss 每个线程的堆栈大小

  • -XX:NewSize 设置年轻代大小(for 1.3/1.4)

  • -XX:MaxNewSize 年轻代最大值(for 1.3/1.4)

  • -XX:NewRatio 年轻代与年老代的比值(除去持久代)

  • -XX:SurvivorRatio Eden区与Survivor区的的比值

  • -XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。

  • -XX:MaxTenuringThreshold设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代

垃圾收集器相关

  • -XX:+UseParallelGC:选择垃圾收集器为并行收集器。

  • -XX:ParallelGCThreads=20:配置并行收集器的线程数

  • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。

  • -XX:CMSFullGCsBeforeCompaction=5 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行5次GC以后对内存空间进行压缩、整理。

  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

辅助信息相关

  • -XX:+PrintGCDetails 打印GC详细信息

  • -XX:+HeapDumpOnOutOfMemoryError 让JVM在发生内存溢出的时候自动生成内存快照,排查问题用

  • -XX:+DisableExplicitGC 禁止系统`System.gc()``,防止手动误触发FGC造成问题.

  • -XX:+PrintTLAB 查看TLAB空间的使用情况

jvm体系结构?

  • jvm指令集

  • 类加载器

  • 执行引擎-也相当于是jvm的cpu

  • 内存区-jvm的存储单元

  • 本地方法调用-调用c、c++实现的本地方法

引用类型?

无论是可达性分析法还是引用计数法,垃圾回收都与引用类型有关

  • 强引用:被引用的对象不会被回收 常见 new

Object obj = new Object();

  • 弱引用:weakRefrence,被关联的对象在下次垃圾收集之前被回收

Obejct oj = new Object();

WeakReference<Obejct> wf = new WeakReference<Obejct>(oj);

oj = null; System.gc(); //下面会发现有时候直接返回null;wf.get();

  • 软引用:softRefrence,被关联的对象内存不足时会被回收

SoftReference<String> ref = new SoftReference<String>("hong");

  • 虚引用:这个引用也有人叫幻引用,也很明显,引用一个不存在,随时会被干掉,算是所有引用中最容易被干掉的。也就是随时可能被回收

// 虚引用

Object oj= new Object();

ReferenceQueue req= new ReferenceQueue();

PhantomReference<Object> pr= new PhantomReference<Object>(oj, req);

// 每次返回Null

System.out.println(pr.get());

//返回是否被删除

System.out.println(pr.isEnqueued());

猜你喜欢

转载自blog.csdn.net/qq_31671187/article/details/128632661
今日推荐