JVM虚拟机(一)介绍、JVM内存模型、JAVA内存模型,堆区、虚拟机栈、本地方法栈、方法区、常量池

news2025/1/9 19:54:42

目录

学习JVM有什么用、为什么要学JVM?

JVM是什么呢?

优点一:一次编写,到处运行。(Write Once, Run Anywhere,WORA)

优点二:自动内存管理,垃圾回收机制。

优点三:多线程支持

【Java程序运行机制】我们首先需要了解java如何运行起来的?

一、JVM和JMM的区别

(1)Java内存模型(JMM)

(2)JVM内存模型(JVM)

二、JVM运行时数据区

一、堆(heap)--- 存放对象实例(重要)

二、虚拟机栈(JVM Stack)--- (线程私有)

三、本地方法栈

四、方法区(Non-Heap)==> 永久代(1.8以前)、元空间(1.8以后)

五、常量池

(1)字符串常量池(String Pool)---堆区

(2)运行时常量池---方法区


学习JVM有什么用、为什么要学JVM?

  1. 深入理解Java语言:了解JVM的工作原理可以帮助你更深入地理解Java语言的运行机制,包括字节码的编译和执行过程;以及JVM的一些概念和技术,如垃圾收集、内存管理等。
  2. 改进代码质量:了解JVM的内存模型和并发模型可以帮助你编写更高质量的代码,避免死锁和竞态条件等问题。
  3. 性能优化:掌握JVM的知识能让你更有效地进行性能调优。你可以分析和理解垃圾收集器的行为,优化内存使用,减少延迟和提高吞吐量。
  4. 故障排查:当遇到内存泄漏、栈溢出、CPU飙高 或其他JVM相关的问题时,对JVM的了解可以帮助你快速定位和解决问题。可以更熟练地使用各种JVM工具,如jconsole、jstack、jmap、jstat等,这些工具对于监控和故障排查非常有用。
  5. 准备面试:对于高级Java开发者或架构师的职位,对JVM的理解通常是面试中的重要部分

JVM是什么呢?

JVM(Java Virtual Machine,Java虚拟机)是一个可以执行Java字节码的虚拟计算机。它是Java平台的核心组成部分


优点一:一次编写,到处运行。(Write Once, Run Anywhere,WORA)

Java源代码被编译成平台无关的字节码,可以在任何安装了JVM的平台上(windows/Linux/mac)运行。

优点二:自动内存管理,垃圾回收机制。

JVM负责自动内存管理,包括垃圾收集(Garbage Collection,GC),减轻了开发者管理内存的负担。

说到这里,一般会跟 C语言进行对比,C语言需要程序员自己去管理内存,如果程序员由于编码不当,很容易造成内存泄露的问题。而 Java 虚拟机的垃圾回收功能就大大减轻了程序员的负担,减少了程序员出错的机会。

优点三:多线程支持

JVM支持多线程编程,允许并发执行,提高了应用程序的效率。

【Java程序运行机制】我们首先需要了解java如何运行起来的?

Java文件是如何运行起来的:

Java文件运行起来的过程通常涉及以下几个步骤: 

1、编写代码:首先,我们编写Java源代码【后缀为.java

2、编译代码:使用JDK中的Java编译器(javac命令)将源代码编译成字节码【.class文件

3、运行程序:使用Java虚拟机(JVM)java命令运行生成的字节码文件。这将加载字节码并执行其中的代码。

4、类加载JVM首先加载字节码文件。类加载器(ClassLoader)首先加载.class文件,然后链接阶段会进行验证、准备和解析。最后,在初始化阶段,JVM会执行类构造函数<clinit>()方法。

5、执行主方法:JVM查找并执行main方法,这是程序的入口点。在执行期间,JVM管理多个运行时数据区,包括方法区、堆、栈、本地方法栈和程序计数器。

6、程序结束:当程序完成执行或遇到退出命令时,JVM将停止程序,并进行必要的清理工作。

7、垃圾回收JVM的垃圾收集器会自动回收不再使用的对象所占用的内存。

在整个过程中,Java代码的安全性由JVM的安全管理器来保证,确保代码不会执行任何违反安全策略的操作。

总结:

其实运行一个Java程序都是通过启动一个JVM虚拟机,在虚拟机里面运行XXX.class文件

C:\Program Files\Java\jdk1.8.0_333\bin\java

一、JVM和JMM的区别

(1)Java内存模型(JMM)

Java内存模型规定所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本的拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

Java 内存模型(下文简称 JMM)就是在底层处理器内存模型的基础上,定义自己的多线程语义。它明 确指定了一组排序规则,来保证线程间的可见性。

java 提供了几种语言结构,包括 volatile, final 和 synchronized, 它们旨在帮助程序员向编译器描述程序 的并发要求,其中:

  • volatile - 保证可见性和有序性

  • synchronized - 保证可见性和有序性; 通过管程(Monitor)保证一组动作的原子性

  • final - 通过禁止在构造函数初始化和给 final 字段赋值这两个动作的重排序,保证可见性(如果 this 引用逃逸就不好说可见性了)

(2)JVM内存模型(JVM)

Jvm:(java Virtual Machine)JVM是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

JVM主要组成部分:JVM包括两个子系统和两个组件

  • 类加载子系统

  • 执行引擎

  • 运行时数据区

  • 本地接口

二、JVM运行时数据区

一、堆(heap)--- 存放对象实例(重要)

Java 堆是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

  • 【堆的物理空间是不连续的所以分配的内存是在运行期确认的,因此大小不固定】

  • 存放内容:堆存放的是对象的实例和数组。

大部分Java的内存溢出都属于堆溢出。原因是因为大量对象占据了堆空间,这些对象都持有强引用导致无法回收。

例子:

启动参数设置-Xmx10m,限制最大的内存为10M

public static void main(String[] args){
​
   List<String> list = new ArrayList<>();
   while(true){
       list.add("111");
   }
} 

二、虚拟机栈(JVM Stack)--- (线程私有)

每个线程运行时所需要的内存,称为虚拟机栈,它的特点就是先进后出。

【栈的物理空间是连续的,所以分配的内存大小要在编译期就确认,大小是固定的】

栈存放的内容:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。

  • 每个线程运行的时候都会创建虚拟机栈,所以栈内存也是线程安全的。

  • 每个栈由多个栈帧(frame)组成,对应着每次方法调用时所需要的数据,或者说占用的内存

JVM栈主要负责以下几个方面:

  1. 方法调用:每当Java程序调用一个方法时,JVM栈就会为这次方法调用创建一个栈帧(Stack Frame),用于存储局部变量、操作数栈、动态链接信息和方法出口等。

  2. 局部变量存储:每个栈帧都包含一个局部变量表,用于存储方法中的局部变量,包括基本数据类型、对象引用以及returnAddress类型(指向方法调用结束后的执行点)。

  3. 操作数栈:每个栈帧还包含一个操作数栈,用于存储方法执行过程中的中间结果,以及参与计算的操作数。

  4. 同步:JVM栈还与Java同步机制有关。当一个同步方法或同步块被调用时,JVM会创建一个锁记录(Lock Record),并将其压入当前线程的JVM栈中。

  5. 异常处理:JVM栈还参与异常处理。当异常发生时,JVM会创建一个异常对象,并在JVM栈中查找匹配的异常处理器。

  6. 线程私有每个线程都有自己的JVM栈,这意味着JVM栈是线程私有的内存区域,用于支持多线程环境下的并发执行。

  7. 内存管理:JVM栈中的栈帧随着方法调用的结束而自动被销毁,其内存由JVM自动管理,不需要程序员手动释放。

Java 虚拟机栈会出现两种异常:StackOverFlowError 和 OutOfMemoryError:

  • StackOverFlowError:当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。(典型的场景有:递归调用和死循环)

  • OutOfMemoryError:若 Java 虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了(如果JVM栈的内存空间不足以创建新的栈帧),此时抛出OutOfMemoryError异常。

由于每一个线程的开启都要占用系统内存,因此当线程数量太多时,也有可能导致OOM。由于线程的栈空间也是在堆外分配的,因此和直接内存非常相似,如果想让系统支持更多的线程,那么应该使用一个较小的堆空间。

三、本地方法栈

和虚拟机栈所发挥的作用非常相似,区别是:

  • 本地方法栈则为虚拟机使用到的 Native 方法服务。

  • 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。方法执行完毕后相应的栈帧也会出栈并释放内存空间

本地方法栈报错:

也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。

本地方法区都有什么里面都有什么?

native方法

四、方法区(Non-Heap)==> 永久代(1.8以前)、元空间(1.8以后)

(线程共享)

它用于存储已被虚拟机加载的:类信息、常量、静态变量、方法字节码、即时编译器编译后的代码等数据。

方法区的两个实现类:

  • JDK6、JDK7 时,方法区 就是 PermGen(永久代)。

  • JDK8 时,方法区就是 Metaspace(元空间)

下面是关于方法区的介绍:

方法区(Method Area) 是各个线程共享的内存区域(跟我们之前讲过的堆空间是一样的)。

元空间默认空间大小是21M,如果空间不足会触发 Full GC,然后扩容。

主要存储类的信息、运行时常量池。

方法区是在虚拟机启动的时候创建,关闭虚拟机时释放元空间的内存。

如果方法区域中的内存无法满足分配请求,则会抛出 OutOfMemoryError: Metaspace。

方法区的实现运行时常量池字符串常量池
Jdk6PermGen space(永久代)永久代---在堆区永久代--在堆区
Jdk7PermGen space(永久代)永久代-方法区在堆区
Jdk8Metaspace(元空间)元空间-方法区在堆区

在 jdk1.8中元空间替代了永久代,原方法区被分成两部分:

方法区的Class文件信息,Class常量池和运行时常量池的三者关系

  • 加载的类信息

  • 运行时常量池:加载的类信息被保存在元数据区中,运行时常量池保存在堆中;

五、常量池

每个class一份,存在于字节码文件中。常量池中有字面量(数量值、字符串值)和符号引用(类符号引用、字段符号引用、方法符号引用),虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型

运行时和字符串常量池的版本变化? 存在哪里?

  • 在JDK1.7之前:运行时常量池--- 包含字符串常量池存放在方法区(永久代)

  • 在JDK1.7:字符串常量池被从永久代拿到了中, 运行时常量池还在方法区, 也就是hotspot中的永久代。

  • 在JDK1.8:hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)

(1)字符串常量池(String Pool)---堆区

字符串常量池又称为:字符串池,全局字符串池,英文也叫String Pool。 在工作中,String类是我们使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,这就是我们今天要讨论的核心:字符串常量池。字符串常量池由String类私有的维护。

字符串常量池

每个JVM中只有一份,存在于堆区。全局字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的)。 在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(用双引号括起来的引用而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。

(2)运行时常量池---方法区

行时常量池它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到常量池中。

运行时常量池是当Class文件被加载到内存后,Java虚拟机会将Class文件常量池里的内容转移到运行时常量池里(运行时常量池也是每个类都有一个)。运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中

运行时常量池

每个class一份,存在于方法区中(元空间)。当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是下面的StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

总结:

JVM之 方法区、永久代(PermGen space)、元空间(Metaspace)三者的区别猎人在吃肉的博客-CSDN博客方法区和元空间区别

通过上面分析,大家应该清楚了 JDK8 中永久代向元空间的转换。不过大家应该都有一个疑问,就是为什么要做这个转换?所以,最后给大家总结以下几点原因:

1)字符串存在永久代中,容易出现性能问题和内存溢出。

2)类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

4)Oracle 可能会将HotSpot 与 JRockit 合二为一。

整理完毕,下一节继续!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2040681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IOS 03 纯代码封装自定义View控件

本节将通过纯代码进行封装自定义View控件&#xff0c;以常用的设置页的item为例&#xff0c;实现UI效果如下&#xff1a; 1、创建SettingView继承自UIView import UIKitclass SettingView: UIView {} 2、重写 init() 和 required init?(coder: NSCoder) 方法 纯代码创建Set…

仿RabbitMq实现消息队列正式篇(虚拟机篇)

TOC目录 虚拟机模块 要管理的数据 要管理的操作 消息管理模块 要管理的数据 消息信息 消息主体 消息的管理 管理方法 管理数据 管理操作 队列消息管理 交换机数据管理 要管理的数据 要管理的操作 代码展示 队列数据管理 要管理的数据 要管理的操作 代码展示…

PHP转Go系列 | ThinkPHP与Gin框架之打造基于WebSocket技术的消息推送中心

大家好&#xff0c;我是码农先森。 在早些年前客户端想要实时获取到最新消息&#xff0c;都是使用定时长轮询的方式&#xff0c;不断的从服务器上获取数据&#xff0c;这种粗暴的骚操作实属不雅。不过现如今我也还见有人还在一些场景下使用&#xff0c;比如在 PC 端扫描二维码…

浅谈JDK

JDK(Java Development Kit) JDK是Java开发工具包&#xff0c;是Java编程语言的核心软件开发工具。 JDK包含了一系列用于开发、编译和运行Java应用程序的工具和资源。其中包括&#xff1a; 1.Java编译器&#xff08;javac&#xff09;&#xff1a;用于将Java源代码编译成字节…

MS8923/8923S低压、高精度、推挽输出比较器

MS8923/8923S 是一款差分输入、高速、低功耗比较器&#xff0c;具 有互补 TTL 输出。其传输延时在 10ns 左右&#xff0c;输入共模范围可以 到负轨。 MS8923/8923S 可以在线性区保持输出稳定特性&#xff0c;单电 源供电是 5.0V &#xff0c;双电源供电是 5V 。 MS89…

【算法/学习】:记忆化搜索

✨ 落魄谷中寒风吹&#xff0c;春秋蝉鸣少年归 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;算法学习 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f44…

数据结构——队列的讲解(超详细)

前言&#xff1a; 我们在之前刚讲述完对于栈的讲解&#xff0c;下面我们在讲另一个类似栈的数据结构——队列&#xff0c;它们都是线性表&#xff0c;但结构是大有不同&#xff0c;下面我们直接进入讲解&#xff01; 目录 1.队列的概念和结构 1.1.队列的概念 1.2.队列的结构 2.…

基于Python的去哪儿网数据采集与分析可视化大屏设计与实现

摘要 本文旨在介绍如何利用Python进行去哪儿网景点数据的采集与分析。通过采集去哪儿网上的景点数据&#xff0c;我们可以获取大量的旅游相关信息&#xff0c;并基于这些数据进行深入分析和洞察&#xff0c;为旅游行业、市场营销策略以及用户个性化推荐等提供支持。 本文将使用…

MySQL(DQL)

一&#xff0c;SQL语言分类 &#xff08;1&#xff09;数据查询语言&#xff08;DQL&#xff1a;Data Query Language&#xff09;其语句&#xff0c;也称为 “数据检索语句”&#xff0c;用以从表中获得数据&#xff0c;确定数据怎样在应用程 序给出。关键字 SELECT 是 DQL&a…

Python打包命令汇总

1、pyinstaller打包 环境安装&#xff1a;pip install pyinstaller 网络不好可以通过 -i 指定安装源&#xff1a;pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple/安装完成后通过&#xff1a;pyinstaller --version 查看是否安装成功 打包单个脚本&…

操作系统笔记二

虚拟内存 把不常用的数据放到硬盘上去&#xff0c;常用的代码或者数据才加载到内存&#xff0c;来实现虚拟的大内存的感觉 覆盖技术 目标&#xff1a;在较小内存运行较大程序。 原理&#xff1a;把程序按自身逻辑结构划分若干功能上相对独立的程序模块。不回同时执行的模块共…

FreeRTOS学习笔记(一)—— 裸机和RTOS,Freertos移植(MDK),stm32cubeIDE使用Freertos

FreeRTOS学习笔记&#xff08;一&#xff09;—— 裸机和RTOS&#xff0c;Freertos移植&#xff08;MDK&#xff09;&#xff0c;stm32cubeIDE使用Freertos 文章目录 FreeRTOS学习笔记&#xff08;一&#xff09;—— 裸机和RTOS&#xff0c;Freertos移植&#xff08;MDK&#…

uniapp/vue个性化单选、复选组件

个性化单选和复选组件在网页设计中非常常见&#xff0c;它们不仅能够提升用户界面的美观度&#xff0c;还能改善用户体验。此组件是使用vue uniapp实现的个性化单选复选组件。设计完成后&#xff0c;点击生成源码即可。 拖动组件过设计区 每行显示数量 默认支持每行三个&#…

Maven-学习首篇

目录 Maven简介基本概念&特点Maven的安装与配置Maven基础概念及使用方法Maven的项目结构Maven的使用Maven的依赖管理Maven的生命周期和插件常见疑问Maven的插件机制是如何工作的&#xff1f;Maven的POM文件主要包含哪些内容&#xff1f;Maven的生命周期包括哪些阶段&#x…

【C++语言】list的构造函数与迭代器

1. list的介绍及使用 1.1 list的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点…

C++中的IO流

目录 1.C语言的输入与输出 2.流是什么 3.CIO流 标准IO流 IO流的四个标志 C文件IO流 4.stringstream的简单介绍 1.C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键 盘)读取数据&#xff0c;并将值存放在变…

钢铁百科:A572Gr60和SA572Gr60材质分析、A572Gr60和SA572Gr60简介

A572Gr60和SA572Gr60是两种常用的结构钢板&#xff0c;它们在材质、执行标准、化学成分、力学性能、交货状态、应用范围和常用规格方面有所不同。 材质&#xff1a; A572Gr60&#xff1a;属于美国材料与试验协会&#xff08;ASTM&#xff09;标准下的A572系列高性能结构钢&…

UIAbility组件基础(一)

一、概述 UIAbility组件是一种包含UI的应用组件&#xff0c;主要用于和用户交互。UIAbility组件是系统调度的基本单元&#xff0c;为应用提供绘制界面的窗口。一个应用可以包含一个或多个UIAbility组件。每一个UIAbility组件实例都会在最近任务列表中显示一个对应的任务。 U…

自研低代码海报制作平台学习分享计划

vue3组件库开发前面咱卷完了JuanTree组件&#xff0c;接下来一起来卷vue3低代码海报制作平台的基础组件实现。首先是拖拽基础组件的开发&#xff0c;整好把前面学习的知识点再运用进来。 文章目录 效果演示基本拖拽区域拖拽旋转其他效果待实现 录屏说明 看一步步实现的效果&…

C++--类和对象(二)

类和对象的基础定义可参看&#xff1a;C--类和对象&#xff08;一&#xff09;-CSDN博客 本篇讲述类和对象里相当重要的几个成员函数 目录 类的默认成员函数&#xff1a; 1.构造函数 2.析构函数 3.拷贝构造函数 &#xff08;1&#xff09;无限递归调用拷贝构造 &#xff…