JAVA虚拟机相关总结

JAVA虚拟机相关总结

基础知识

详细总结见博文

也可以通过下面这个图复习:
在这里插入图片描述

1. Java 内存区域

Java运行时内存区域分为:

  • 程序计数器(PCR):记录各线程正在执行的虚拟机指令地址。
  • 虚拟机栈:线程私有,随线程创建,用栈帧(Frame)的方式储存局部变量表、操作数栈、动态链接方法出口等信息。方法从调用到完成,对应栈帧的入栈和出栈。其中局部变量表包括基本数据类型和引用。在class的编译阶段就确定了局部变量表的分配。
  • 本地方法栈:同虚拟机栈,为本地方法所有。(java方法为java编写的class文件,本地方法为系统固有的dll方法)。
  • java堆:所有线程共享;用于存放对象实例;属于垃圾回收器的主要管理区域;堆可根据JVM具体设计,细分为线程私有,用于提高回收和分配的效率。
  • 方法区:所有线程共享;用于存放类型信息、常量、静态变量等数据,其中运行时常量池存放编译期生成的各种字面量和符号引用。编译器和运行期(String 的 intern() )都可以将常量放入池中。内存有限。(静态、常量、方法各种属性信息等)
  • 直接内存:非虚拟机运行时数据区的部分,通过本地方法(dll)调用。

1.1 虚拟机对象的创建

  1. 遇到 new 指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,执行相应的类加载。
  2. 为新对象分配内存(内存大小在类加载完成后便可确认)。在堆的空闲内存中划分一块区域(‘指针碰撞-内存规整’或‘空闲列表-内存交错’的分配方式)。
  3. 每个线程在堆中都会有私有的分配缓冲区(TLAB),这样可以很大程度避免在并发情况下频繁创建对象造成的线程不安全。
  4. 内存空间分配完成后会初始化为 0(不包括对象头),接下来就是填充对象头,把对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息存入对象头。
  5. 执行 new 指令后执行 init 方法后才算一份真正可用的对象创建完成。

1.2 虚拟机对象的存储

  • 对象头:一个固定长度(32位或64位)的二进制串,根据不同对象确定每一位的意义,类似于网络数据包的报头信息。
  • 实例数据:程序代码中所定义的各种类型的字段内容(包含父类继承下来的和子类中定义的)。
  • 对齐填充(Padding):不是必然需要,主要是占位,保证对象大小是某个字节的整数倍。

1.3 虚拟机对象的访问定位

  • 通过句柄访问:虚拟机栈引用–> 句柄池–>获得到对象实例数据的指针,获得到对象类型数据(方法区)的指针。
  • 直接访问:虚拟机栈引用–> 获得到对象实例数据,获得到对象类型数据(方法区)的指针。
    比较:使用句柄的最大好处是 reference 中存储的是稳定的句柄地址,在对象移动(GC)是只改变实例数据指针地址,reference 自身不需要修改。直接指针访问的最大好处是速度快,节省了一次指针定位的时间开销。如果是对象频繁 GC 那么句柄方法好,如果是对象频繁访问则直接指针访问好。

1.4 内存溢出异常

  • 不断创建对象,导致堆溢出
  • 在方法中定义一大堆变量,导致栈溢出
  • 通过不算intern导致方法区和常量池溢出

2. 垃圾回收器与内存分配策略

程序计数器、虚拟机栈、本地方法栈 3 个区域随线程生灭(因为是线程私有),栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而 Java 堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期才知道那些对象会创建,这部分内存的分配和回收都是动态的,垃圾回收期所关注的就是这部分内存。垃圾回收是自动进行的,但是会成为系统达到高并发量的瓶颈。

2.1 如何判断对象已死

  • 引用计数法:对象每一次被引用(如A=B中B被引用),计数加一,每次引用失效(例如A被赋值为其他对象),计数减一,当计数为零时,回收对象。问题在于难以解决循环引用问题。
  • 可达性分析算法:通过一系列的 ‘GC Roots’ 的对象作为起始点,从这些节点出发所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连的时候说明对象不可用。‘GC Roots’通过专门的枚举算法获得,具体包括虚拟机栈中引用的对象,方法区中类静态属性引用的对象,静态方法中常量引用的对象,本地方法栈中引用的对象。

对于回收方法区,需回收废弃的常量和无用的类。对于常量一般是判断没有该常量的引用。对于类,该类所有的实例都已经回收,也就是 Java 堆中不存在该类的任何实例;加载该类的 ClassLoader 已经被回收;该类对应的 java.lang.Class 对象没有任何地方呗引用,无法在任何地方通过反射访问该类的方法。

2.2 垃圾收集算法

  • 标记-清除法:直接标记清除,效率不高且产生大量空间碎片。
  • 分代回收 :根据存活对象划分几块内存区,一般是分为新生代和老年代。然后根据各个年代的特点制定相应的回收算法。其中新生代一般回收较频繁,老年代反之。年代的划分要尽量减少跨代的引用,跨代引用可以采用记忆集的方式记录。
  • 复制算法:把空间分成两块,每次只对其中一块进行 GC。当这块内存使用完时,就将还存活的对象复制到另一块上面。这样保证剩余空间连续,但是加大内存消耗。可以在回收较为频繁的新生代使用,这样保留区较小,万一不够还可以临时申请老年区空间。现有复制算法可以分一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。当回收时,将 Eden 和 Survivor 中还存活的对象一次性复制到另一块 Survivor 上,最后清理 Eden 和 Survivor 空间。
  • 标记-整理法:针对老年代的特点,创建该算法。主要是把存活对象移到内存的一端。这样会加大整理所带来的所有进程中断时间,但是程序吞吐量高的时候能提高访问效率(对象储存不连续的话加大了访问代价)。

2.3 HotSpot算法细节

  • 枚举根节点:要找到引用分析的所有根节点,保证一致性(分析的时候各线程不能改变引用关系),因此会对各线程带来停顿。利用OopMap记录和快速查找当前上下文的引用。
  • 安全点与安全区域:线程不断轮询中断标志,发生中断时要停在安全点或安全区域,保证引用关系不发生变化,只有中断标志恢复后才能离开安全点或安全区域。
  • 记忆集与卡表
  • 写屏障
  • 并发的可达性分析

2.3 G1垃圾收集器

一款面向服务端应用的垃圾收集器。

特点如下:

  • 并行与并发: G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
  • 分代收集: G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
  • 空间整合: G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。
  • 可预测的停顿: G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

3 Java 类的加载

编译原理简单过程为词法分析 --> 语法分析 --> 语义分析和中间代码生成 --> 优化 --> 目标代码生成,详见编译原理
Java程序从源文件创建到程序运行要经过两大步骤:.java—(编译)—.class字节码文件—(JVM)—目标代码

3.1 类的加载过程

加载 --> 连接 --> 初始化

  1. 加载: 通过一个类的全限定名来获取定义此类的二进制字节流(Class文件);将这个二进制字节流所代表的静态存储结果转化为方法区的运行时数据结构;在内存中生成一个java.lang.Class对象,注意:存放在方法区!详见下文介绍。
  2. 连接: 链接主要分为验证、准备、解析三个过程,其中验证目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求。准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。首先这时候进行内存分配的仅包括类变量(static修饰的变量),而不是实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,在类初始化的时候才会将value的值赋为123。解析过程是虚拟机将class常量池内的符号引用替换为直接引用的过程,见下文详细介绍。
  3. 初始化: 类初始化阶段是类加载过程的最后一步;在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源:初始化阶段是执行类构造器( )方法的过程。详见下文介绍。
3.1.1 类的加载详解

对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等。

面试题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值