JVM快速入门

本文详细介绍了JVM的结构,包括类加载器、运行时数据区、执行引擎、本地库接口和本地方法库。重点讲解了运行时数据区的各个部分,如方法区、堆、虚拟机栈、本地方法栈和程序计数器,以及类加载器的双亲委派机制。此外,还涉及了native关键字、元空间以及垃圾回收算法。通过对JVM的深入理解,有助于提升Java程序的性能优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JVM快速入门

一.JVM的位置

在这里插入图片描述
二.JVM 可分为 5 个部分,分别是:

1、类加载器(Class Loader)

2、运行时数据区(Runtime Data Area)

3、执行引擎(Execution Engine)

4、本地库接口(Native Interface)

5、本地方法库(Native Libraies)

这其中最复杂的是运行时数据区,又可分为方法区、虚拟机栈、本地方法栈、堆、程序计数器,并且方法区和堆是线程共享的,虚拟机栈、本地方法栈、程序计数器是线程隔离的,JVM 的结构如下图所示。
在这里插入图片描述
搞清楚了 JVM 虚拟机的结构,接下来我们详细讲解它的每一部分。
类加载器:加载字节码文件到内存。
执行引擎:对 JVM 指令进行解析,翻译成机器码,解析完成后提交到操作系统中。
本地库接口:供 Java 调用的融合了不同开发语言的原生库。
本地方法库:Java 本地方法的具体实现。
运行时数据区:JVM 核心内存空间结构模型。

运行时数据区是 JVM 内存结构最重要的部分,接下来我们详细讲解运行时数据区的各个组成部分。

类加载器

在这里插入图片描述
加载器类型从上往下:

  • 1.虚拟机自带的加载器
  • 2.启动类(根)加载器(Bootstrap classLoader)
  • 3.扩展类加载器(ExtClassLoader)
  • 4.应用程序加载器(AppClassLoader)

过程:请求是从下往上委托,加载是从上往下

  1. 类加载器收到类加载的请求。
  2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器。
  3. 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子加载器进行加载。
  4. 重复步骤3

什么是双亲委派机制?

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

双亲委派机制的作用

  1. 防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
  2. 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

native关键字

介绍:

  1. native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
  2. JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。

用法:

  1. 编写带有native声明的方法的Java类(java文件)
  2. 使用javac命令编译编写的Java类(class文件)
  3. 使用javah -jni ****来生成后缀名为.h的头文件(.h的文件)
  4. 使用其他语言(C、C++)实现本地方法
  5. 将本地方法编写的文件生成动态链接库(dll文件)

PC寄存器

程序计数器:Program Counter Register

  • 每个线程都有一个程序计数器,是线程私有的,就是一个指针。程序计数器占用的内存空间较小,是当前线程所执行的字节码行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令。多个线程之间的程序计数器相互独立,互不影响,为了保证每个线程都恢复后都可以找到具体的执行位置。

方法区

Method Area 方法区

  • 方法区是被所有线程共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口方法都在此定义。简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;

  • 静态方法,常量,类信息(构造方法,接口定义),运行时的常量池存在方法区中(static,final,Class,常量池),但是实例变量存在堆内存中,和方法区无关。

  • 方法区是一种规范,永久代是方法区的一种实现,这里有个常考的面试题:JDK 7 以前的版本字符串常量池是放在永久代中的,JDK 7 将字符串常量池移动到了堆中,JDK 8 直接删除了永久代,改用元空间替代永久代。

Java 堆

  • Java 堆用来存放实例化对象,Java 堆被所有线程共享,在虚拟机启动时创建,用来存放对象实例,是 Java 内存结构中的大头,占用大部分的空间,是 GC 的主要管理区域,又可分为年轻代、老年代、永久代,JDK 8 及以后去掉了永久代。

年轻代(新生区)

  • 年轻代又可分为 Eden(伊甸园),from Survivor(幸存者 from),to Survivor(幸存者 to)。

  • Eden区:对象刚被创建的时候,存放在 Eden 区,如果 Eden 区放不下,则放在 Survivor 区,甚至老年代中。

  • Survivor 区:Survivor 又可分为 Survivor From 和 Survivor To,GC 回收时使用,将 Eden
    中存活的对象存入 Survior From 中,下一次回收时,将 Survior From 中的对象存入 Survior To 中,清除 Survior From ,下一次回收时重复次步骤,Survior From 变成 Survior To,Survivor To 变成 Survivor From,依次循环,同时每次回收,对象的年龄都 +1,年龄增加到一定程度的对象,移动到老年代中。

老年代(养老区)

  • 存放生命周期较长的对象。JDK 8 之后改用元空间替代永久代。

元空间(JDK8以前叫永久存储区)

  • Java 8 之后开始将类的元数据放在堆内存中,这块区域叫做元空间,在 Java 7 及以前,元空间是放在永久代中的,Java 8之后分离出来了。

  • 元空间和永久代是方法区的实现,方法区只是一种规范,在 Java 7 之后,原先位于方法区永久代里的字符串常量池已被移动到了 Java堆中,因为永久代的内存空间极为有限,如果频繁调用 inter 方法,内存无法存储这么多数据。在 Java 8之后将永久代完全删除了,使用元空间替代了永久代。

  • 元空间使用本地内存,永久代使用 JVM 内存,所以使用元空间的好处在于程序的内存不在受限于 JVM内存,本地内存剩余多少空间,元空间就可以有多大,解决了空间不足的问题。

GC垃圾回收,主要是在伊甸园区和养老区。

GC算法

  • 引用计数法
  • 复制算法
  • 标记清除法
  • 标记压缩法

内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法= 标记压缩算法 > 标记清除法
内存利用率:标记压缩算法 = 标记清除算法 >复制算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值