java程序在运行过程各个内部结构的作用

news2025/1/11 4:18:34

一:内部结构

一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器、本地方法栈和虚拟机栈接下来我们通过一个示意图介绍一下这个空间。

如图所示,当一个helloword程序编译成为可以运行的二进制编码程序helloword.class程序的时候,运行二进制文件,整个进程会放到一个本地电脑的内存环境中去,其中,一个程序就是一个JVM实例,代码中包含定义方法的方法区域,开辟空间的堆空间区域,以及控制好程序的运行走向的程序计数器,以及方法调用时候用到的本地方法栈,虚拟机栈等,在代码运行的过程中我们知道代码会产生很多的垃圾,这些垃圾是需要JVM中的垃圾回收器去回收的她不像C或CPP那样手动的垃圾清除,在JAVA中一个JVM空间会有一个GC垃圾回收器,通过各种有效的算法帮我们实现垃圾的自动清除,后面我会介绍GC如何实现垃圾回收机制的。

二:程序计数器

程序计数器是什么?相信大家第一次听到这个话题的时候应该很敏感,其实博主是嵌入式转JAVA的所以对于底层还是颇有了解的。

  • 程序计数器是一块较小的内存空间,它可以看作是:保存当前线程所正在执行的字节码指令的地址(行号),简单来说就是记住我现在是哪个线程在具体的运行,保存一下

  • 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,一个处理器都只会执行一条线程中的指令。(这里我来说明一下如下图所示,线程轮转简单来说就是时间片轮转,因为对于单核处理器而言,处理并发线程,就是在分配时间片给到每个并发线程不停地切换任务,所以所谓的并发并不是真的同时进行,只是无限接近并发而已,但是我要说明一下时间片轮转速度是非常快的,很多人疑问这个轮转速度是由什么保证的呢?----这就要考虑到硬件支持了,博主也是电子专业对其有一些了解,下次我再给你们介绍,现在画个饼哈哈哈)因此,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储(如图所示,程序计数器及记录着每个线程运行到哪了,这样下次时间片轮转再次轮到你,你可以接着运行),称之为“线程私有”的内存。程序计数器内存区域是虚拟机中唯一没有规定OutOfMemoryError情况的区域。

不知道通过上面的例子大家有没有理解,不理解的话我下面举个例子:

线程A在看直播,突然,线程B来了一个视频电话,就会抢夺线程A的时间片,就会打断了线程A,线程A就会接电话,然后,视频电话结束,这时线程A究竟该干什么? (线程是最小的执行单位,他不具备记忆功能,他只负责去干,那这个记忆就由:程序计数器来记录)这样视频电话结束了以后又可以回到直播继续播放

三:栈

  1. java虚拟机是线程私有的,它的生命周期和线程相同

  2. 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息

  3. 如图所示,其中每个栈信息的属性解释

  • 局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型(returnAddress中保存的是return后要执行的字节码的指令地址)

  • 操作数栈:操作数栈就是用来操作的,例如代码中有个 i = 6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去

  • 动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方意思就是要在方法中转化到另外一个方法中去,这样就需要动态衔接

  • 出口:出口是什呢,出口正常的话就是return 不正常的话就是抛出异常落

如何设置栈的内存大小?

使用参数-Xss选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。 (IDEA设置方法:Run-EditConfigurations-VM options 填入指定栈的大小-Xss256k)

思考:

一个方法调用另一个方法,会创建很多栈帧吗?

答:会创建。如果一个栈中有动态链接调用别的方法,就会去创建新的栈帧,栈中是由顺序的,一个栈帧调用另一个栈帧,另一个栈帧就会排在调用者下面

栈指向堆是什么意思?

栈指向堆是什么意思,就是栈中要使用成员变量怎么办,栈中不会存储成员变量,只会存储一个应用地址,堆中的数据等下讲

递归的调用自己会创建很多栈帧吗?

递归的话也会创建多个栈帧,就是一直排下去,沒有結束条件就会产生oostack的风险,下面的例子就是爆栈的实际例子:

public class StackOverFlowErrorTest {
    public static void main(String[] args) {
    //递归调用main
    main(args);}
}

四:堆

  • java堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例

  • 在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配

  • java堆是垃圾收集器管理的主要区域,因此也被称为“GC堆”(后面在说GC垃圾回收机制的时候你就会明白这个名词)

  • 从内存回收角度来看java堆可分为:新生代和老生代

  • 从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。

  • 无论怎么划分,都与存放内容无关,无论哪个区域,存储的都是对象实例,进一步的划分都是为了更好的回收内存,或者更快的分配内存(其实都在堆区域,只不过为了加快回收算法就细化了一些区域,毕竟堆空间区域很大)

  • 根据Java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间中(正因为这个不连续性所以我们堆空间的位置都需要一个指针去维护它,不然很容易丢失,导致堆空间无法释放,内存泄漏)。当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

堆的细分内存结构

一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速(存入新生代)

另外一类对象时生命周期非常长,在某些情况下还能与JVM的生命周期保持一致 (存入老年代)

Java堆区进一步细分可以分为新生代(YoungGen)和老年代(OldGen),新生代可以分为伊甸园区(Eden)、新生区1(from)和新生区2(to)

JDK8及以后

为什么要把Java堆分代?不分代就不能正常工作了么?

经研究,不同对象的生命周期不同。70%-99%的对象都是临时对象,新生代:有Eden、Survivor构成(s0,s1 又称为from to),to总为空,老年代:存放新生代中经历多次依然存活的对象。

其实不分代完全可以,分代的唯一理由就是优化GC性能。 如果没有分代,那所有的对象都在一块,就如同把一个学校的人都关在一个教室。 GC的时候要找到哪些对象没用,这样就会对堆的所有区域进行扫描,而很多对象都是朝生夕死的。 如果分代的话,把新创建的对象放到某一地方,当GC的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。

永久代为什么要被元空间替换?

由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。

这项改动是很有必要的,原因有:

(1)为永久代设置空间大小是很难确定的。

在某些场景下,如果动态加载类过多,容易产生Perm区的O0M,比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。 “Exception in thread’ dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace”而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

(2)对永久代进行调优是很困难的

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

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

相关文章

在Linux中进行Redis的yum安装与配置

redis安装在IP为x.x.x.x的服务器上 redis是使用内存作为主存,使用硬盘来实现数据持久化,而且redis是周期性的将数据写到硬盘上。这就意味着一旦服务器出现断电、重启之类的情况,很可能会出现数据丢失的情况,因此不建议使用redis来…

直播美颜插件详解:美颜SDK技术探讨

今天,小编将深入讲解美颜插件与美颜SDK技术,对其原理和应用进行详细解析。 一、美颜SDK技术简介 美颜SDK专门用于实现美颜功能。其核心技术包括图像处理算法和人工智能技术。美颜SDK可以实时捕捉摄像头画面,识别人脸,然后对人脸…

【附带源码】机械臂MoveIt2极简教程(三)、URDF/SRDF介绍

系列文章目录 【附带源码】机械臂MoveIt2极简教程(一)、moveit2安装 【附带源码】机械臂MoveIt2极简教程(二)、move_group交互 【附带源码】机械臂MoveIt2极简教程(三)、URDF/SRDF介绍 目录 系列文章目录1. URDF1.1 URDF相关资源1.2 小技巧1.2.1 关节名称1.2.2 安全限制…

云计算-期末复习题-选择/判断/填空/简答(1)

目录 填空题/简答题 单选题 多选题 判断题 云计算期末复习部分练习题,下一章会补全。祝大家好好复习,顺利通过课程。 填空题/简答题 >保障云基本安全的对策包括()、()和() &…

[AI Omost] 革命性AI图像合成技术,让你的创意几乎一触即发!

介绍 Omost 是一个创新的项目,它利用大型语言模型(LLM)的编码能力来生成图像,特别是通过图像合成技术。项目的名称“Omost”发音类似于“almost”,寓意着使用该项目后,用户的图像生成工作几乎可以完成。同…

超详解——python函数定义和调用——小白篇

目录 1. 函数的参数 位置参数: 关键字参数: 默认参数: 2. 关键字参数 3. 参数组 4. 函数重载 5. 函数的返回值 单个返回值: 多个返回值: 6. 函数的内置属性 __name__: __doc__: 7…

重温react-02

shopdemo import React, { Component } from react export default class shopDemo extends Component {state {goods: [{id: 1,name: 商品1,price: 100,number: 0,money: 0},{id: 2,name: 商品2,price: 200,number: 0,money: 0},{id: 3,name: 商品3,price: 300,number: 0,mo…

如何解决 Git 默认不区分文件名大小写和同名文件共存?

修改文件命名的大小写,不会有 git 记录 本文章的例子:将 demo.vue 文件命名改为 Demo.vue 1、在Git项目路径下执行该命令 git config core.ignorecase false (1)以上方法可以实现 git 区分 demo.vue 与 Demo.vue 文件&#xff0…

【投稿优惠|权威主办】2024年物联网、土木建筑与城市工程国际学术会议(ICITCEUE 2024)

2024年物联网、土木建筑与城市工程国际学术会议(ICITCEUE 2024) 2024 International Academic Conference on the Internet of Things, Civil Engineering and Urban Engineering(ICITCEUE 2024) ▶会议简介 2024年物联网、土木建…

【解读】核密度图

def:what 核密度估计(Kernel Density Estimation,简称KDE)是一种用来估计随机变量概率密度函数的非参数方法 实现:(库函数)how import seaborn as sns import matplotlib.pyplot as plt# 使用…

开源完全自动化的桌上足球机器人Foosbar;自动编写和修复代码的AI小工具;开源工具,可本地运行,作为Perplexity AI的替代方案

✨ 1: Foosbar Foosbar是一款完全自动化的桌上足球机器人,能与人类玩家对战,具备防守、传球和射门能力。 Foosbar是一个完全自动化的桌上足球机器人,它实现了一侧由机器人控制,另一侧由人类玩家对战的游戏模式。这个机器人能够自…

2024下半年软考高级没有高项,考哪门最容易上岸?

2024上半年软考考试已经结束,有不少小伙伴已经开始准备下半年软考了,但是大家要注意:今年高项仅考上半年一次,下半年考的高级科目只有这四个(系分、架构、网规、系规)! 一、那么这种情况下&…

go语音进阶 多任务

多任务 什么叫 多任务?简单说:就像是操作系统可以同时执行 多个任务。打个比方 你一边使用 浏览器上网,一遍在听MP3, 一边再用 word 赶作业。对于电脑来讲这就是多任务,还有很多任务悄悄的在后台同时运行着,只是桌面上…

npm 添加 electron 安装镜像变量,提交打包速度。

前言:项目中使用 electron-builder,打包运行 npm run build:win 时, electron-builder 默认会从 github 下载 electron 依赖包,导致打包缓慢。可以通过添加 electron 下载镜像地址来解决。 npm config ls -l 查看 npm 所有配置 …

SmartEDA:革新教育电路,点亮学生创新之光!

在当今快速发展的科技时代,电路设计与电子技术的融合已成为教育领域不可或缺的一部分。而SmartEDA作为一款新兴的电路设计工具,正以其独特的优势助力学生创新实践,引领教育界迈向更加智能化的未来。 SmartEDA不仅具备传统电路设计软件的基本…

Redis(十六) 集群

文章目录 前言什么是集群集群模式基本原理哈希求余一致性哈希算法哈希槽分区算法 docker模拟出一个集群集群中节点挂了会怎么办故障判定故障迁移 集群扩容 前言 前面我们学习了 redis 哨兵机制,哨兵机制是为了解决当主节点挂了之后,能够自动进行故障转移…

JVM对象分配和垃圾回收机制

一、对象创建 1.1 符号引用 new 创建一个对象,需要在JVM创建对象。 符号引用:目标对象采用一个符号表示,类A加载的时候,如果成员变量类B还没有被加载进来,采用一个符号(字面量)来表示&#x…

Linux C语言:指针的运算

一、指针的算术运算 1、指针运算 指针运算是以指针所存放的地址作为运算量而进行的指针运算的实质就是地址的计算 2、指针的算数运算 指针加上整数,指针减去整数, 指针递增,指针递减和两个指针相减。 指针加减一个n的运算: px n px - n 移动步长…

LeetCode | 2879.显示前三行

在 pandas 中,可以使用 head() 方法来读取 DataFrame 的前几行数据。如果想读取指定数量的行,可以在 head() 方法中传入一个参数 n,读取前 n 行 import pandas as pddef selectFirstRows(employees: pd.DataFrame) -> pd.DataFrame:retur…

mybatisplus(原理)使用方法引用的形式获取实体类对应数据库的列名

我们现在正常来看 一个mybatisplus正常的查询语句 我们可以看到 ,再如上的代码中 我们使用了 Address::getuserId 方法引用,但是我们把方法引用改成lambda表达式的形式的时候不会报错,但是运行的时候报错。为什么…