JVM内部世界(内存划分,类加载,垃圾回收)(上)

简介: JVM内部世界(内存划分,类加载,垃圾回收)

💕"Echo"💕

作者:Mylvzi

文章主要内容:JVM内部世界(内存划分,类加载,垃圾回收)

关于JVM的学习主要掌握三方面:

  1. JVM内存区的划分
  2. 类加载
  3. 垃圾回收

一.JVM内存区的划分

当一个Java进程开始执行时,JVM会首先向操作系统申请一块较大的内存来提供进程在执行过程中所需的空间,而JVM为了更加高效,规范化的管理数据,将这块内存划分为5个区域

  1. 方法区/元数据区
  2. 栈区
  3. 堆区
  4. 程序计数器
  5. 本地方法区

1.方法区/元数据区

主要存放与类相关的信息,如静态变量,方法等

Java中的一个(.class)文件在运行时就会被加载为一个类对象,类对象中包含与类相关的数据和方法,这些信息都被存储到元数据区(方法区)中

2.堆区

存放实例化的对象(new)

存储实例化出的对象,包括对象中包含的实例变量(成员变量)

注:实例方法是存储在方法区之中的,属于类对象的信息

3.程序计数器

存放进程在执行过程中的字节码指令在方法区的地址或者当前正在执行的方法的地址

程序计数器是JVM内存中占用内存比较小的一部分区域,当一个Java进程运行时,文件中包含的代码就都被转化为字节码指令,字节码指令是JVM可以识别和执行的最小单位,通过字节码指令来完成代码中的逻辑

程序计数器的一个很大的用途是用在多线程之中,一个进程包含多个线程,每一个线程都有自己的私有的程序计数器,用于存储当前线程的执行指令,当由一个线程跳转到另一个线程时,需要保存当前线程的执行的指令的位置,以便跳转回该线程时能够继续执行代码,程序计数器就起到了这样的作用,由此也可以看出,程序计数器也是线程安全的

4.栈区

用于存放局部变量和方法的调用关系,每调用一次方法,就会在栈区中创建出栈帧,来表示一个方法调用,随着方法的执行完毕,栈帧又会从栈区之中脱离(出栈)

5.本地方法区

存储本地方法

在Java中,有很多方法的底层是通过C++进行编写的,在源码中我们无法看到背后的具体执行逻辑,但是在开发中也会使用,所以就划分出本地方法区专门存储这些方法

一个经典面试题:

分别说出一下三个变量在内存中的位置

class Test {
  public int a;
  public static int b;
}
Test t = new Test();
  • a:通过new Test()创建,和new出来的对象一样位于堆区之中(成员变量)
  • b:静态变量,位于方法区
  • t:引用变量,位于栈区之中

注意:

Test t = new Test();

t并不是我们实例化出的对象,而是一个引用,真正的对象是在堆区中存储的,t就类似于C语言中的指针,用于指向实例化的对象,但t本身并不是对象,仅仅是一个引用类型的变量

总结:

每个线程都有自己私有的栈空间和程序计数器,同一个进程里的所有线程共用方法区和堆区

找到垃圾的方式有两种:

  1. 引用计数
  2. 可达性分析

可达性分析的核心是通过一组线程周期性的扫描所有的对象,在一次扫描过程中,如果扫描到了对应的对象,就标记为可达,表示该对象仍然存在,如果没有扫描到,JVM就会执行回收

内部是通过一个N叉树的方式来组织各种对象的,通过扫描这棵树的方式来进行可达性的分析

二.类加载

类加载部分主要掌握两部分:

  1. 类加载的过程
  2. 双亲委派模型

1.类加载过程

类加载就是.class文件被JVM转换为类对象的过程

在完成源代码的编写之后,源代码会被转换为字节码文件,这些文件通常以.class作为后缀,JVM需要读取到这个.class文件并将其转换为类对象,并保存到方法区中才能运行程序

所谓程序运行,执行代码本质上就是要执行方法,要执行方法首先要知道这些方法的指令(字节码),而这些指令是和创建的类紧密相连的

类加载的过程分为5步:

1.加载

分为三步:

  1. 找到.class文件
  2. 打开.class文件
  3. 读取.class文件

2.验证

对于生成的.class文件,JVM是有着严格的格式规范的,JVM在读取.class文件之后,首先会对格式进行验证,具体的格式在Java的标准文档上有介绍

3.准备

为类对象分配内存,注意这里仅仅只是分配内存,并没有进行初始化

4.解析

符号引用 --> 直接引用

文件偏移量 --> 内存地址

Java源代码中的字符串引用也会被保存到.class文件之中:

类似:

String s = "hello";

引用s在代码中实际上是存储的字符串的地址,引用s被加载到.class文件之中,在文件里面是没有地址这个概念的,但是初始化s就必须要指明其所指向的对象的地址,在文件系统里,我们通过引用和指向对象之间的距离文件偏移量来替代地址这个概念

比如当引用s被保存到.class文件之中,和其指向的字符串"hello"在文件中的存储位置相差1000,则文件偏移量就是1000

5.初始化

类对象初始化 把各个属性初始化好 还需要初始化static成员,静态代码块,加载父类

2.双亲委派模型

在上面类加载的第一步中,第一步找到.class文件是一个比较繁琐的过程,在Java中,通过类加载器来完成寻找.class文件的过程,类加载器是JVM的一个模块,内置了三个类加载器帮助我们完成找.class文件的过程,分别是:

注:上面的三个类加载器并不是继承关系,之所以叫爷父子是因为每个类加载器中都有一个属性parent,这个属性指向上一级的加载器

完整过程:

  1. 给一个全限定类名,作为寻找的依据(比如java.lang.String)
  2. 以Application ClassLoader为入口,但是先不从自己的库中寻找(负责当前项目的库和第三方库),而是先交给Extension ClassLoader加载器
  3. Extension ClassLoader加载器也不会直接在自己的库中寻找(负责JDK的扩展库),而是先交给BootStrap ClassLoader类加载器
  4. 同样的BootStrap ClassLoader也不会直接在自己的库中寻找,而是交给自己的父加载器,但是并没有父加载器,就只能在自己的库中寻找,如果找到了,就执行加载操作的剩余步骤,如果没找到就交给子加载器(Extension ClassLoader)
  5. Extension ClassLoader此时就会从自己的库中寻找对应的.class文件,如果找到了,执行加载操作的剩余步骤,没找到,交给子加载器(Application ClassLoader)
  6. Application ClassLoader从自己的库中寻找,如果找到了,执行加载操作的剩余步骤,没找到,就会抛出ClassNotFound异常

以上就是查找.class文件的完整过程,上述寻找的过程就被称为双亲委派模型,这里的双亲其实是翻译问题,英文是parent,而不是parents,应该翻译为双亲之一,实际上在上述类加载器中,也只有父子这种关系

双亲委派模型实际上就是一个优先级问题,是为了保证标准库中的类先被加载,其次是扩展库,最后才是当前项目和第三方库

比如你自己写了一个形如java.lang.String的类,在加载时会首先从标准库中寻找,而不是你自己的项目库

实际上,双亲委派模型也不是不能打破的,比如tomcat服务器,在进行类加载时只会在webapp目录中寻找,如果没找到,也不会从其他地方寻找

JVM内部世界(内存划分,类加载,垃圾回收)(下)https://developer.aliyun.com/article/1480775?spm=a2c6h.13148508.setting.19.361f4f0eyTL4lb

目录
相关文章
|
22天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
45 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
12天前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
37 10
|
11天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
18天前
|
存储 监控 算法
Java中的内存管理与垃圾回收机制解析
本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
|
21天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
45 2
|
19小时前
|
JavaScript 前端开发 Java
垃圾回收机制会导致内存泄漏吗?
【10月更文挑战第29天】虽然JavaScript的垃圾回收机制本身是为了有效地管理内存,但开发者在编写代码时需要注意上述这些可能导致内存泄漏的情况,遵循良好的编程习惯,及时释放不再使用的资源,以确保程序能够高效地利用内存资源,避免出现内存泄漏问题。
|
19天前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
21天前
|
存储 监控 算法
深入理解Java内存模型与垃圾回收机制
【10月更文挑战第10天】深入理解Java内存模型与垃圾回收机制
16 0
|
22天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
33 4
|
3月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。