JVM总结

1.类的加载过程
类的加载过程分为三个阶段:加载、链接以及初始化。
的主要任务是通过类的全限定类名获取定义此类的二进制字节流,将该字节流所代表的静态存储结构转化为方法区的运行时数据结构并在内存中生成一个代表该类的Class对象.
的主要任务是验证字节码文件是否符合要求并对类变量初始化,将常量池内的符号引用转化为直接引用的过程.
阶段就是执行类构造器过程,当代码中存在静态变量的赋值以及静态代码块才有该方法,若该类具有父类,jvm会保证子类的执行前,父类方法执行完毕

2.对象的创建方式
<1>.通过new关键字的方式
<2>.通过Constructor的newInstance方式
<3>.使用clone():这种方式不调用任何构造器,当前类需要实现cloneable接口,实现clone
<5>.使用反序列化:从文件或网络中获取一个对象的二进制流

3.对象的创建过程
假设通过new关键字来创建对象,1.当虚拟机遇到new指令,首先会判断对象对应的类是否加载,如果没有则需要通过类加载器来加载
<2>.为对象分配内存空间:计算对象占用空间大小,并在堆中划分一块内存给新对象
<3>.对对象属性进行默认的初始化
<4>.设置对象的对象头
<5>.执行类的构造方法(init方法)进行初始化

4.在堆中为对象划分空间有两种不同的方式?
如果堆内存是规整的话,采用指针碰撞
如果堆内存不规整,则采用空闲列表的方式
指针碰撞:指的的是内存空间由空闲内存和使用过内存组成并且中间存在一个指针来区分二者,分配内存就是把指针向空闲那边移动一段与对象大小相等的距离。

5.java对象头里有什么?
对象头包含两个部分:运行时元数据(哈希值,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳)以及类型指针(指向类元数据,确定该对象的所属的类型)

对象的内存布局:对象头,实例数据,对齐填充(不是必须的,也没特别含义,仅仅起到占位符的作用)

6.对象的访问定位
建立对象就是为了使用对象,我们的 Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有①使用句柄和②直接指针两种:
: 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息;
: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象的地址。
这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。

7.JVM的内存结构,以及每个区放什么
JVM内存结构分为两大类,一类是线程独占的有程序计数器,本地方法栈和虚拟机栈;一类是线程共享的有方法区和堆。方法区是java虚拟机的一个模型规范,具体实现有元空间和永久代。永久代是1.7版本以及之前,1.8之后永久代被移除了,然后就变成了元空间,元空间是发布在计算机内存的,它脱离了java虚拟机内存独立存在。

方法区:主要存储类信息,常量池(static常量和static变量)
堆:用于存放所有对象的实例和数组
java虚拟机栈:栈存储数据以栈帧为基本单位,调用一个方法就压入一个栈帧,帧上面存储局部变量表,操作数栈,方法出口等信息
本地方法栈:和虚拟机栈作用类似,它主要是为native方法服务
程序计数器:记录当前线程执行的行号

7.Minor GC和Full GC有什么不同呢?
首先jvm将堆空间分为了新生代和老年代。
新生代GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC经常会伴随至少一次的Minor GC(并非绝对),Major GC的速度一般会比Minor GC的慢10倍以上。

8.如何判定对象是否死亡
有两种算法:<1>.:给对象添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不能再被使用的。
这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。 所谓对象之间的相互引用问题,如下面代码所示:除了对象objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知 GC 回收器回收他们。
<2>.:这个算法是首先需要确定一个GC Roots集合,从这些节点开始从上往下搜索,节点走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

(1)虚拟机栈中引用的对象
(2)方法区中类静态属性引用的对象。比如:java类的引用类型静态变量
(3)方法区中常量引用的对象。比如:字符串常量池里的引用
(4)本地方法栈中native方法中引用的对象
(5)所有被同步锁synchronized所持有的对象
(6)java虚拟机内部的引用。基本数据类型对应的Class对象

9.简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。
强引用是对象中最常见的一种引用。比如使用new来创建一个对象,然后将该对象的地址赋值给一个变量,那么该对象就具备一个强引用并且它不会被垃圾收集器回收。
软引用:它是一种可有可无的引用。当内存空间足够时,该对象不会被回收;当内存空间不足时该对象会被回收。
弱引用:和软引用类似,不同的是它拥有更短的生命周期。如果垃圾回收线程发现了只具有弱引用的对象,那么不管内存空间是否足够都会回收该对象。
虚引用:它不会决定对象的生命周期。它的主要作用是用来跟踪对象被垃圾回收的活动并且它需要和引用队列联合使用,当垃圾收集器准备回收一个对象时,如果发先它存在虚引用,就会在回收对象之前把这个虚引用加入到引用队列中,这样程序可以通过判断队列中是否加入虚引用,在对象被回收之前采取必要的措施。

10.如何判断一个常量是废弃常量

11.如何判断一个类是无用的类

12.垃圾收集有哪些算法,各自的特点?
(1)标记-清除算法
当堆中的有效内存空间被耗尽的时候,会停止整个程序,然后进行两项工作,第一个是从根节点开始遍历,对所有被引用的对象标记。第二是清除未被标记的垃圾对象,但这里的清除并不是真正的清除而是维持一个空闲列表,把需要清除对象地址保存在空闲的地址列表例。
(2)复制算法
因为标记清除算法的两项工作都需要遍历所有对象,效率低。为了解决该算法的低效率问题,提出了复制算法。主要思想:将可用的内存空间分为两块,每次使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的对象并将两个内存的角色互换。
缺点:内存代价太高,需要牺牲一半的内存,并且两个空间复制对象其地址会改动还需要维护指针的调整
(3)标记-整理算法
为了解决标记-清除算法产出的内存碎片问题。它也分为两个阶段。第一阶段和标记清除算法一样,标记被引用的对象。第二阶段是将所有被引用对象压缩到内存的一端,按顺序排放,之后清理边界所有空间对象。
缺点:效率低于复制算法,移动对象需要维护指针的调整,移动过程需要全程暂停用户应用程序
(4)分代垃圾回收算法
分代算法是前面算法的组合。不同生命周期的对象可以采取不同的收集算法。根据对象的生命周期的不同,堆空间被分为新生代和老年代,由于新生代的对象存活时间短,存活率低,所以适合高效率的复制算法。老年代对象生命周期长,存活率高,回收不频繁,一般使用标记清除算法或者标记整理算法或者二者的混合

13.HotSpot 为什么要分为新生代和老年代?
为了更好的回收内存或者更快的分配内存

14.常见的垃圾回收器有那些?
串行收集器serial:它是最早的垃圾收集器,在进行垃圾收集工作的时候会暂停其他所有的工作线程,然后使用一条垃圾收集线程去完成垃圾收集工作,直到收集结束。但长时间的停顿会导致用户的体验效果比较差。
并行分类器(ParNew, Parallel Scavenge):它和serial收集器差不多,不同的是在进行垃圾收集工作的时候采用了多条垃圾收集线程
CMS:它第一次实现了让垃圾收集线程与用户线程并发工作。它在进行垃圾回收的时候,有四个步骤:
(1)初始标记:暂停其他所有的线程,并记录直接与根对象集合直接相连的对象,速度很快。
(2)并发标记:同时开启GC和用户线程并记录当前所有可达对象。因为用户线程可能不断更新引用域,所以GC线程无法保证可达性分析的实时性,但是会记录引用被更新的地方。
(3)重新标记:也会暂停其他所有的线程,然后修正并发标记期间发生引用更新的记录。
(4)并发清除:开启用户线程,同时GC线程对未标记的区域做清扫。
CMS并发收集器采用的是标记-清除算法,所以在进行垃圾回收的时候会产生内存碎片

G1:采用G1收集器的内存空间被分为了多个大小相同的内存块Region。它的运作大致也分为四个步骤:
(1)初始标记
(2)并发标记
(3)最终标记
(4)筛选回收
在进行筛选回收的时候,G1收集器实际上是在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region。

15.堆内存常见的分配策略
java自动内存管理最核心的功能是堆内存中对象的分配与回收。
堆内存常见的分配策略:对象优先在eden区分配、大对象直接进入老年代、长期存活的对象进入老年代

你可能感兴趣的:(JVM总结)