探索JAVA神秘运行机制:揭秘JVM内存区域

news2024/11/27 20:35:58

目录

1. 前文回顾

2.内存区域的划分

2.1 存放类的方法区

2.2  程序计数器

2.3 Java虚拟机栈

2.4 Java堆内存

2.5  其他内存区域

3. 核心内存区域运行流程

4. 总结

1. 前文回顾

        上一篇我们一起探索了Java的整体运行流程,类加载器以及类的加载机制,了解了从编译打包后生成的字节码文件,JVM启动后,类的加载时机,加载之后验证,准备,解析都是做什么的,以及尤为重要的是准备阶段和初始化阶段,是如何为类分配内存空间的?然后类加载器的规则是什么?来看下下图,简单回顾下内容

2.内存区域的划分

        接上一篇的问题,我们编写的类在内存中到底是什么样子的,又是如何进行划分的呢?在看之前想一个问题,我们都知道,就是我们要用的类通过类加载器加载后,在内存中会分配一块空间出来进行存储,我们看一个类,主要分为类名称、属性、方法、代码块这几个部分,那么他们在内存中是如何存储的,是统一的放到一起,还是分开来放的?我们执行的代码,调用的方法是不是要一个个的执行他们在内存中是如何进行的,方法中的变量是不是要放到一起?我们带着这些问题,一起来看下内存区域的划分。

2.1 存放类的方法区

        这个方法区是在JDK1.8以前的版本里,代表JVM中的一块区域。主要是放从".class"文件里加载进来的类,还会有一些类似常量池的东西放在这个区域里。但是在JDK1.8以后,这块区域的名字改了,叫做"Metaspace",可以认为是"元数据空间”这样的意思。当然这里主要还是存放我们自己写的各种类相关的信息(主要是包括类信息、常量、静态变量等类的结构信息)。其实,我倒是认为jdk1.8 升级后,改的名字更加的贴切,元数据信息空间,也就是主要存储类的元数据信息。

2.2  程序计数器

        程序计数器这个的概念理解,其实说的简单点就是:我们写好的Java代码会被翻译成字节码,对应各种字节码指令。所以当JVM加载类信息到内存之后,实际就会使用自己的字节码执行引擎,去执行我们写的代码编译出来的代码指令,而在执行的过程中,由于是多个线程执行的,为了保证每个线程执行的不错误,能够方便的知道执行到那个字节码位置,需要有一个东西来记录下他们执行的位置,这个就引出来“程序计数器” 这一个概念了。那么我们现在明确了"程序计数器”这个概念:用来记录当前执行的字节码指令的位置的,也就是记录目前执行到了哪一条字节码指令。

2.3 Java虚拟机栈

        这个Java虚拟机栈概念也很好理解,我先一步一步的引入这个概念,首先我们都知道Java代码在执行的时候,一定是线程来执行某个方法中的代码,就是最主要的main线程执行main()方法的代码指令的时候,也会通过main线程对应的程序计数器记录自己执行的指令位置。但是呢,在执行方法内部是时候,我们通常会定义一些局部变量出来,此时问题来,我们用的这些局部变量,每个线程执行这个方法的时候都会使用这个局部变量,是不是要有个地方用来储存吧,因此,JVM必须有一块区域是来保存每个方法内的局部变量等数据的,这个区域就是Java虚拟机栈。

每个线程都有自己的Java虚拟机栈,比如这里的main线程就会有自己的一个Java虚拟机栈,用来存放自己执行的那些方法的局部变量。如果线程执行了一个方法,就会对这个方法调用创建对应的一个栈帧,栈帧里面包含的有方法的局部变量表、操作数栈、动态链接、方法出口等东西,这里大家先不用全都理解,我们先关注局部变量。

        比如main线程执行了main()方法,那么就会给这个main()方法创建一个栈帧,压入main线程的Java虚拟机栈,同时在main()方法的栈帧里,会存放对应的"replicaManager"局部变量,我们称之为把局部变量压入栈帧, 等到方法执行完毕后,局部变量就会从这个Java虚拟机栈里面出栈。这个就是我们平常使用方法内部的局部变量的执行过程。

        简单的理解这个是我们写的类里面执行的方法,中间使用到的局部变量这些东西,有个客栈或者理解成工具箱,专门储存这些东西,等到方法执行的时候,会把局部变量进来,等待使用,使用完了就移除来了,这个过程就是局部变量的入栈出栈。

2.4 Java堆内存

        我们前面说了,Java的类结构等信息存在了方法区,线程执行的字节码执行的位置信息在程序计数器中,类的方法内局部变量信息在运行时,存储在Java虚拟机栈中,那么还剩下什么呢?我们类写好了,也都已经加载完了,但是我们还有个操作是什么呢?对象的实例化,对象实例化我们放在那呢?JVM也为我们想到了,这块区域就是堆内存。Java堆内存,这里就是存放我们在代码中创建的各种对象的

public class Kafka {
    public static void main(String[] args){
        ReplicaManager replicaManager = new ReplicaManager();
        replicamanager.loadReplicasFromDisk();
    }
}

        我们在代码中用的时候呢,其实是结合着来的,Java堆内存是存放实例化的对象,例如,我们写的代码执行的main() 方法,里面的局部变量被放入到了Java虚拟机栈中,然后又进行了实例化对象,Java堆内存区域里会放入类似ReplicaManager的对象,然后我们因为在main方法里创建了ReplicaManager对象的,那么在线程执行main方法代码的时候,就会在main方法对应的栈顿的局部变量表里,让一个引用类型的"replicaManager"局部变量来存放ReplicaManager对象的地址。 相当于你可以认为局部变量表里的"eplicaManager"指向了Java堆内存里的ReplicaManager对象。

2.5  其他内存区域

        上面说了平常我们主要用到的一些区域,我们其实还有其他的一些区域,比如IO相关的,NIO相关的,网络Socket相关的,如果大家去看他内部的源码,会发现很多地方都不是Java代码了,而是走的native方法去调用本地操作系统里面的一些方法,可能调用的都是c语言写的方法,或者一些底层类库,比如下面这样的:public native int hashCode(); 在调用这种native方法的时候,就会有线程对应的本地方法栈,这个里面也是跟Java虚拟机栈类似的,也是存放各种native方法的局部变量表之类的信息。

还有一个区域,是不属于JVM的,通过NIO中的alocateDirect这种AP,可以在Java堆外分配内存空间。然后,通过Java虚拟机里的DirectByteBuffer来引用和操作堆外内存空间。其实很多技术都会用这种方式,因为有一些场景下,堆外内存分配可以提升性能。

3. 核心内存区域运行流程

        jvm内存区域的划分,我们已经讲完了,那么我串起来整体过下他的流程,这样就更能加深印象,先来一个图:

根据上图我们再来一段代码,整体的说下流程:

public class Kafka {
    public static void main(String[] args){
        ReplicaManager replicaManager = new ReplicaManager();
        replicamanager.loadReplicasFromDisk();
    }

    public class ReplicaManager{
        private long replicaCount;
        public void loadReplicasFromDisk(){
            Boolean hasFinishedLoad = false;
            if(isLocalDatacorrupt()) {}
        }
        public Boolean isLocalDatacorrupt(){
            Boolean isCorrupt = false;
            return isCorrupt;
        }
    }
}

        我们一起来看下上面的过程,首先,你的JVM进程会启动,就会先加载你的Kaika类到内存里。然后有一个main线程,开始执行你的Kaika中的main()方法。main线程是关联了一个程序计数器的,那么他执行到哪一行指令,就会记录在这里。其次,就是main线程在执行main()方法的时候,会在main线程关联的Java虚拟机栈里,压入一个main()方法的栈帧。接着会发现需要创建一个ReplicaManager类的实例对象,此时会加载ReplicaManager类到内存里来。然后会创建一个ReplicaManager的对象实例分配在Java堆内存里,并且在main()方法的栈帧里的局部变量表引入一个“replicaManager”变量,让他引用ReplicaManager对象在Java堆内存中的地址。接着,main线程开始执行ReplicaManager对象中的方法,会依次把自己执行到的方法对应的栈顿压入自己的Java虚拟机栈,执行完方法之后再把方法对应的栈顿从Java虚拟机栈里出栈。

        其实大家理解了这个过程,那么JVM中的各个核心内存区域的功能和对应的我们的Java代码之间的关系,就彻底理解了。

4. 总结

        好了,最后我们做个总结,JVM为我们把各个地方的区域都做好了设计,我们最后来梳理下:(1)方法区:Java类的结构信息,元数据存储区域;

       (2)程序计数器:用来记录当前执行的字节码指令的位置的;

       (3)Java虚拟机栈:用来保存每个方法内的局部变量等数据的;

       (4)Java堆内存:用来存放我们在代码中创建的各种对象的;

       (5)其他的内存区域:本地方法栈、堆外内存;

经过内存区域的划分了解,我们知道了JVM是如何对内存进行划分的,了解了Java的类中各个部件在内存中是如何存储的?我们解决了存储的问题。那么,他在运行过程中又是如何进行调用这些数据的,调用过程中又是如何进行回收的呢?会不会出现内存溢出,又是如何处理的呢?我们带着这些疑问,等到下一篇文章在说,JVM的垃圾回收机制来解答。

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

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

相关文章

手把手教你使用 VS Code 运行和调试 Python 程序

本文以 Ubuntu 系统为例,介绍如何在 VS Code 上配置 Python 的编程环境,并把 Python 程序运行、调试起来。由于 Python 是解释型语言,并且 VS Code 中提供了内置的调试器可用于调试 Python 代码,因此配置和操作流程比调试 C/C 代码…

【 Qt 快速上手】-①- Qt 背景介绍与发展前景

文章目录 1.1 什么是 Qt1.2 Qt 的发展史1.3 Qt 支持的平台1.4 Qt 版本1.5 Qt 的优点1.6 Qt的应用场景1.7 Qt的成功案例1.8 Qt的发展前景及就业分析行业发展方向就业方面的发展前景 1.1 什么是 Qt Qt 是一个跨平台的 C 图形用户界面应用程序框架。它为应用程序开发者提供了建立…

8 python快速上手

总结 总结1. 代码规范1.1 名称1.2 注释1.3 todo1.4 条件嵌套1.5 简单逻辑先处理1.6 循环1.7 变量和值 2.知识补充2.1 pass2.2 is 比较2.3 位运算 3.阶段总结 各位小伙伴想要博客相关资料的话关注公众号:chuanyeTry即可领取相关资料! 总结 1. 代码规范 …

线性规划案例分享

今天想写一个最优传输的简单实现,结果学歪了,学到线性规划去了,这里我发现了一个宝藏网站 虽然是讲计量经济的,但是里面提供的公式和代码我很喜欢,有时间可以好好读一下 https://python.quantecon.org/lp_intro.html …

Civil 3D安装教程,免费使用,带安装包和工具,一分钟轻松搞的安装

前言 Civil 3D是一款面向基础设施行业的建筑信息模型(BIM)解决方案。它为基础设施行业的各类技术人员提供了强大的设计、分析以及文档编制功能,广泛适用于勘察测绘、岩土工程、交通运输、水利水电、市政给排水、城市规划和总图设计等众多领域…

什么是比特币?

比特币 比特币 (英语:Bitcoin,缩写:BTC )是一种基于 去中心化,采用 点对点网络,开放源代码,以 区块链 作为底层技术的 加密货币。比特币由 中本聪(Satoshi Nakamoto&…

vscode配置web开发环境(WampServer)

这里直接去下载了集成的服务器组件wampserver,集成了php,MySQL,Apache 可能会出现安装问题,这里说只有图上这些VC包都安装了才能继续安装,进入报错里提供的链接 在页面内搜索相关信息 github上不去可以去镜像站 下载…

机器视觉技术与应用实战(平均、高斯、水平prewitt、垂直prewitt、水平Sobel、垂直Sobel、拉普拉斯算子、锐化、中值滤波)

扯一点题外话,这一个月经历了太多,接连感染了甲流、乙流,人都快烧没了,乙流最为严重,烧了一个星期的38-39度,咳嗽咳到虚脱。还是需要保护好身体,感觉身体扛不住几次连续发烧!&#x…

Redis 持久化之 RDB AOF

1、简介 Redis 是一个基于内存的 key-value 类型的 Nosql 数据库,经常用来做缓存操作,但是一旦Redis 宕机,重启之后数据会丢失,因此,需要将内存数据进行持久化,保证服务重启后数据能够恢复之前的状态。Redi…

淘金城镇新人赚钱攻略(定制开发·源码定制智创开发)

​ 在淘金城镇中,玩家可以通过完成任务、升级角色、参与活动等方式获得丰厚的奖励和经验值,这不仅可以提升角色的能力, 还可以让玩家在游戏中获得更多的乐趣。最重要的是,淘金城镇的玩法非常精致,玩家可以通过游戏中…

C++——vector的使用及其模拟实现

vector的使用及其模拟实现 文章目录 vector的使用及其模拟实现1. vector的使用1.1 构造函数construct1.2 获取当前存储的数据个数size()和最大容量capacity()1.3 访问1.3.1 operator[]运算符重载1.3.2 迭代器访问1.3.3 范围for 1.4 容量相关reserve()和resize()1.5 增&#xff…

软件测试的工作描述

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

【算法练习Day50】下一个更大元素II接雨水

​📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:练题 🎯长路漫漫浩浩,万事皆有期待 文章目录 下一个更大元素II接雨水单调…

ESP32-CAM带摄像头的开发板使用-环境安装

首先是需要在开发板上搭建环境,其实就是将安装包给下载到开发板上,然后程序能在开发板上运行并控制开发板,这一下载过程也称为烧录。 首先我这里使用ESP32-CAM纯粹是因为便宜,所以买啦 哈哈哈 我买的是30多带摄像头的&#xff0c…

SpringMVC传递数据给前台

SpringMVC有三种方式将数据提供给前台 第一种 使用Request域 第二种 使用Model(数据默认是存放在Request域中) 与第一种方式其实是一致的 第三种 使用Map集合(数据默认是存放在Request域中)

虹科分享 | 汽车技术的未来:Netropy如何测试和确保汽车以太网的性能

文章速览: 什么是汽车以太网?汽车以太网的用途是什么?汽车以太网的测试要求是什么?流量生成如何帮助测试汽车以太网? 如今汽车不再是单纯的代步工具,把人从A点带到B点,同时还配备了车载信息娱乐…

深入数仓离线数据同步:问题分析与优化措施

一、前言 在数据仓库领域,离线数仓和实时数仓是常见的两种架构类型。离线数仓一般通过定时任务在特定时间点(通常是凌晨)将业务数据同步到数据仓库中。这种方式适用于对数据实时性要求不高,更侧重于历史数据分析和报告生成的场景…

大语言模型无代码构建知识图谱概述

2023年3月15日,ChatGPT4.0的横空出世,将人们对大语言模型的关注推到了风口浪尖。由于其在智能问答、翻译以及文本生成等工作任务上的卓越表现,业界一度出现了不再需要发展知识图谱相关技术的观点,知识图谱相关概念严重受挫。无可置…

web学习笔记(十六)

目录 HTML5新增标记汇总 1.新增语义化标签 2.新增音频和视频标签 2.1音频标签 audio 2.1视频标签 video 3.新增图像标签 4.新增表单元素和表单控件 5.新增应用程序标签(使用率较低) HTML5新增标记汇总 1.新增语义化标签 新增语义化标签能够便于…

HCIA-H12-811题目解析(12)

1、如图所示, 关于OSPF的拓扑和配置,下列说法中正确的是? 2、如图所示,私有网络中有一台web服务器需要向公网用户提供HTTP服务,因此网络管理员需要在网关路由器RTA上配置NAT以实现需求,则下面配置中能满足…