JVM常见知识点总结

在经历了两次翻看《深入理解Java虚拟机》之后,康康对JVM总算有了一个较为清晰的认识。本篇文章基于原书内容,对java虚拟机的知识点进行整理,便于复习查看。

一、运行时数据区域

1.线程私有:

a) 程序计数器:当前线程所执行的字节码的行号指示器。占用空间小,不会发生OOM

b) 虚拟机栈:每个方法被执行时,Java虚拟机会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每个方法调用直至执行完毕的过程对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

​ 局部变量表中保存了编译器可知的各种Java虚拟机基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)

c)本地方法栈:与虚拟机栈类似,不过是为Native方法服务

2.线程共享

a)方法区:存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

​ 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池 具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。

b)堆:存储几乎所有的对象实例,通常占用最多的内存,经常发生垃圾回收。

二、简述垃圾回收机制

在 java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚 拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将 它们添加到要回收的集合中,进行回收。

三、GC对象判定机制

1.引用计数法:给每个对象设置一个引用计数器,每当有一个地方引用此对象,计数器加一,若引用失效,计数器减一。当引用计数器为零时,该对象被判定为“已死”,即将被回收

​ 引用计数法易于实现,但是存在循环引用导致内存泄露的问题,主流的虚拟机并没有采用引用计数法

2.可达性算法(引用链法):从GC Roots对象开始向下搜索,如果一个对象到GC Roots没有任何引用链时,说明此对象不可用。

​ GC Roots对象包括以下:

​ a)虚拟机栈中引用的对象

​ b)方法区中静态变量引用的对象

​ c)方法去中常量引用的对象

​ d)本地方法栈中JNI引用的对象

四、垃圾回收算法

1.标记-清除:

​ 这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被 回收的对象,然后统一回收。这种方法很简单,但是会产生大量不连续的内存碎片,导致以后程序在 分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。

2.标记-复制

为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只 使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然 后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。

于是将该算法进行了改进,内存区域不再是按照 1:1 去划分,而是将内存划分为 8:1:1 三部分,较大那份内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。 每次都会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然 后清除 Eden 区,如果此时存活的对象太多,以至于 Survivor 不够时,会将这些对 象通过分配担保机制复制到老年代中。

3.标记-整理

该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高 时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回 收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了

4.分代收集

它根据对象的生存周期,将堆分为新生 代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那 么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担 保,所以可以使用标记-整理 或者 标记-清除

五、简述垃圾回收策略

1.对象优先在堆的Eden区域分配

2.大对象直接进入老年代

3.长期存活的对象逐步进入老年代

4.动态对象年龄判断

5.空间分配担保

六、类加载机制

虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验,准备、解析和初始化,最终形成可以被虚拟机直接使用的 java 类型

具体的类加载过程:

1)加载

加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:

  1. 通过一个类的全限定名获取该类的二进制流。
  2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
  3. 在内存中生成该类的 Class 对象,作为该类的数据访问入口。
2)验证

验证的目的是为了确保 Class 文件的字节流中的信息不回危害到虚拟机

3)准备

准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进 行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象 一起分配在 Java 堆中。

4)解析

该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之 前,也有可能在初始化之后

5)初始化

初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过 自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正 开始执行类中定义的 Java 程序代码

七、类加载器

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

主要有一下四种类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)用来加载 java 核心类库,无法被 java 程序直接 引用。
  2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的 实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH) 来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  4. 用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。

猜你喜欢

转载自blog.csdn.net/weixin_44580146/article/details/109526292