JVM【类加载与GC垃圾回收机制】(上)

简介: JVM【类加载与GC垃圾回收机制】

🍎一.JVM


🍒1.1JVM简介


JVM 是 Java Virtual Machine 的简称,意为 Java虚拟机

虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统

常见的虚拟机:JVM、VMwave、Virtual Box

JVM 和其他两个虚拟机的区别:


  1. VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器
  2. JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进行了裁剪
    JVM 是一台被定制过的现实当中不存在的计算机


🍒1.2JVM执行流程


程序在执行之前先要把java代码转换成字节码(class文件),JVM 首先需要把字节码通过一定的方式类加载器(ClassLoader) 把文件加载到内存中 运行时数据区(Runtime Data Area) ,而字节码文件是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是这4个主要组成部分的职责与功能


162d1e730a424f05969338d98e266bec.png



🍎二.JVM运行时数据区

70dbd571630341ceac64241190b863ea.png

🍒2.1 程序计数器(线程私有)



程序计数器的作用:用来记录当前线程执行的行号的,用来存储下一条指令的地址

程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器值为空


总结:

程序计数器:内存最小的一块区域,保存了下一条要执行的指令地址在哪里,与书签类似


什么是线程私有?

由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令。因此为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们就把类似这类区域称之为"线程私有"的内存


🍒2.2 栈(线程私有)


Java虚拟机栈(线程私有)


Java 虚拟机栈的作用:Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈描述的是 Java 方法执行的

内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息,咱们常说的堆内存、栈内存中,栈内存指的就是虚拟机栈


67398e7986a6463b8a052fb273a4c7dc.png


Java 虚拟机栈中包含了以下 4 部分:


局部变量表: 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表大小。简单来说就是存放方法参数和局部变量

操作栈:每个方法会生成一个先进后出的操作栈。

动态链接:指向运行时常量池的方法引用。

方法返回地址:PC 寄存器的地址

关于虚拟机栈会产生的两种异常:

● 如果线程请求的栈深度大于虚拟机所允许的最大深度,会抛出StackOverFlow异常

● 如果虚拟机在拓展栈时无法申请到足够的内存空间,则会抛出OOM异常


出现StackOverflowError异常时有错误堆栈可以阅读,比较好找到问题所在。如果使用虚拟机默认参数,栈深度在多多数情况下达到1000-2000完全没问题,对于正常的方法调用(包括递归),完全够用


如果是因为多线程导致的内存溢出问题,在不能减少线程数的情况下,只能减少最大堆和减少栈容量的方式来换取更多线程


●本地方法栈

本地方法栈和虚拟机栈类似,只不过 Java 虚拟机栈是给 JVM 使用的,而本地方法栈是给本地方法使用的


🍒2.3 堆(线程共享)


堆:储存对象以及对象的成员变量,一个进程只有一个,多个线程共用一个堆,内存中空间最大的区域,我们看到下图对堆做了细分,Java堆是垃圾收集器管理的内存区域,所以后介绍GC的时候我们细说

763fc3976233433d87f00762e982dc3f.png

🍒2.4 方法区(线程共享)


方法区:存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据,即就是储存“类对象”,被static修饰的变量或方法就成了类属性,.java文件会被编译成.class文件,.class会被加载到内存中,也就被JVM构造成类对象了,这个加载的过程叫做类加载,类对象描述了类的信息,如类名,类有哪些成员,每个成员叫什么名字,权限是什么,方法名等


a43bd7a5b6804ef69c6d2fcfa63e07fa.png

所以可以得到结论,静态的代码块,普通代码块,构造方法执行顺序为:静态的代码块->普通代码块->构造方法


🍎三.JVM类加载


🍒3.1类加载过程


对于一个类来说,它的生命周期是这样的:

d273e3fc87bf4248be4f41f3898a569c.png


其中前 5 步是固定的顺序并且也是类加载的过程,其中中间的 3 步我们都属于连接,所以对于类加载来

说总共分为以下几个步骤:


1. 加载(Loading)
2. 连接(Linking)
  .验证
  .准备
  .解析
3. 初始化(Initialization)


下面我们分别来看每个步骤的具体执行内容

(1) 加载(Loading)

“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段,它和类加载 ClassLoading 是不同的,一个是加载 Loading 另一个是类加载 Class Loading,所以不要把二者搞混了。在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:

● 通过一个类的全限定名来获取定义此类的二进制字节流。

● 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

● 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

81fba31909d44758bd73c0c1e10c1115.png

(2) 验证

主要就是验证读取到的内容是不是和规范中规定的格式完全匹配,如果不匹配,就会类加载失败,并且会抛出异常
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节 流中包含的信息符合《Java虚拟机规范》的全部约束要求,
保证这些信 息被当作代码运行后不会危害虚拟机自身的安全
验证选项:
文件格式验证
字节码验证
符号引用验证...

(3) 准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。
比如此时有这样一行代码:
public static int value = 123;
它是初始化 value 的 int 值为 0,而非 123。

(4) 解析

.class文件中,常量是集中放置的(常量池),并且每一个常量都有一个编号,.class文件中的结构体初始情况
下它只记录了常量的编号,解析过程简单来说就是根据编号将对应的常量填充到类对象中

(5) 初始化(Initialization)

这里是真正地对类对象进行初始化,特别是静态成员
类加载过程是在执行某方法(如main方法)之前执行的,类加载的时候会进行静态代码块的执行,想要创建实例,
必然先得类加载,静态代码块只会执行一次,构造方法与普通代码块每次实例对象都会执行,并且普通代码块比静态代码块先执行

常见笔试题


所以可以得到结论,静态的代码块,普通代码块,构造方法执行顺序为:静态的代码块->普通代码块->构造方法

class A{
    public A(){
        System.out.println("这是A的构造方法");
    }
    {
        System.out.println("这是A的代码块");
    }
    static {
        System.out.println("这是A的静态代码块");
    }
    public void fun(){
        System.out.println("方法A");
    }
}
class B extends A {
    public B() {
        System.out.println("这是B的构造方法");
    }
    {
        System.out.println("这是B的代码块");
    }
    static {
        System.out.println("这是B的静态代码块");
    }
}
public class Test extends B{
    public static void main(String[] args) {
        new Test();
        new Test();
        int s = 10;
        System.out.println(s);
    }
}

57ea65af8f32407a9cbc915821cffbee.png

相关文章
|
17天前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
9天前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
15 0
|
12天前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
27 1
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
73 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
1月前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
2月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
56 3
|
2月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
56 3
|
2月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
2月前
|
算法 Java
谈谈HotSpot JVM 中的不同垃圾回收器
【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
30 1