讲讲 JVM 的内存结构(附上Demo讲解)

讲讲 JVM 的内存结构

  • 什么是 JVM 内存结构?
    • 线程私有
      • 程序计数器​
      • 虚拟机栈
      • 本地方法栈
    • 线程共享
      • 堆​
      • 方法区​
      • 注意
        • 永久代​
        • 元空间​
        • 运行时常量池​
        • 直接内存​
  • 代码详解

什么是 JVM 内存结构?

在这里插入图片描述

JVM内存结构分为5大区域,程序计数器、虚拟机栈、本地方法栈、堆、方法区。​

HotSpot在JDK1.8之前方法区就是永久代,永久代就是方法区。JDK1.8后删除了永久代,改为元空间,元空间在本地内存中。方法区就是元空间,元空间就是方法区。​

创建一个线程,JVM就会为其分配一个私有内存空间,其中包括PC、虚拟机栈和本地方法栈​

简单来说:

  • 堆:存放 new 出来的东西
  • 方法区:被虚拟机加载的类信息、常量、静态常量等。
  • 栈:存放局部变量
  • 程序计数器:记录指令
  • 本地方法栈:Native 方法

接着进行详细的介绍:

线程私有

程序计数器​

线程私有的,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址。程序计数器主要有两个作用:​

  • 当前线程所执行的字节码的行号指示器,通过它实现代码的流程控制,如:顺序执行、选择、循环、异常处理。​
  • 多线程的情况下,程序计数器用于记录当前线程执行的位置,当线程被切换回来的时候能够知道它上次执行的位置。​

程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。​

虚拟机栈

Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次函数调用都会有一个对应的栈帧被压入虚拟机栈,每一个函数调用结束后,都会有一个栈帧被弹出。​

局部变量表是用于存放方法参数和方法内的局部变量。​

每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,在方法调用过程中,会进行动态链接,将这个符号引用转化为直接引用。​

  • 部分符号引用在类加载阶段的时候就转化为直接引用,这种转化就是静态链接​
  • 部分符号引用在运行期间转化为直接引用,这种转化就是动态链接​

Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

Java 虚拟机栈会出现两种错误:StackOverFlowErrorOutOfMemoryError。​
可以通过 -Xss 参数来指定每个线程的虚拟机栈内存大小:java -Xss2M​

本地方法栈

虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。Native 方法一般是用其它语言(C、C++等)编写的。​

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。​

线程共享

堆​

堆用于存放对象实例,是垃圾收集器管理的主要区域,因此也被称作GC堆。​

堆可以细分为:新生代(Eden空间(伊甸园)、From Survivor、To Survivor空间)和老年代。​

通过 -Xms 设定程序启动时占用内存大小,通过 -Xmx 设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。​

方法区​

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。​

对方法区进行垃圾回收的主要目标是对常量池的回收和对类的卸载。​

注意

永久代​

方法区是 JVM 的规范,而永久代PermGen 是方法区的一种实现方式,并且只有 HotSpot 有永久代。对于其他类型的虚拟机,如 JRockit 没有永久代。由于方法区主要存储类的相关信息,所以对于动态生成类的场景比较容易出现永久代的内存溢出。​

元空间​

JDK 1.8 的时候,HotSpot的永久代被彻底移除了,使用元空间替代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

两者最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

为什么要将永久代替换为元空间呢?​
永久代内存受限于 JVM 可用内存,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是相比永久代内存溢出的概率更小。​

运行时常量池​

运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。​

直接内存​

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 错误出现。​

NIO 的 Buffer 提供了 DirectBuffer,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。

DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。​

直接内存的读写操作比堆内存快,可以提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到直接内存。

在这里插入图片描述

代码详解

以下是一个简单的 Java 类,我们将在注释中说明各个内存区域的作用:

public class MemoryStructureExample {
    // 静态变量,存放在方法区
    private static int staticVar = 42;

    public static void main(String[] args) {
        // 局部变量,存放在虚拟机栈
        int localVar = 10;

        // 创建一个对象,存放在堆中
        MemoryStructureExample obj = new MemoryStructureExample();

        // 调用方法,创建一个新的栈帧
        obj.doSomething(localVar);

        // 创建一个数组,存放在堆中
        int[] array = new int[5];

        // 常量,存放在方法区
        final String constantString = "Hello, world!";

        // 本地方法调用,使用本地方法栈
        System.out.println("Static variable: " + staticVar);
        System.out.println("Constant string: " + constantString);
    }

    // 方法,存放在方法区
    public void doSomething(int value) {
        // 局部变量,存放在虚拟机栈
        int result = value * 2;
        System.out.println("Result: " + result);
    }
}

在这段代码中,我们可以看到:

  • staticVar 是一个静态变量,存放在方法区。
  • localVar 是一个局部变量,存放在虚拟机栈。
  • obj 是一个对象,存放在堆中。
  • array 是一个数组,也存放在堆中。
  • constantString 是一个常量,存放在方法区。
  • doSomething 方法创建了一个新的栈帧,其中的局部变量存放在虚拟机栈。

总之,这些内存区域共同构成了 Java 虚拟机的内存结构,确保 Java 程序的正常运行和优化。如果你还有其他问题,欢迎继续提问!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

头歌---数组之Fibonacci数列

一、数组初始化几种方式 1.数组定义时,数组元素全部赋初值 2.部分数组赋初值 >>>>>前三个元素已知初值 >>>>>后三个元素系统自动赋初值为0 注意: 当定义数组时,如果未对它的元素指定过初值,对于内部局部数组…

【openwrt】Openwrt系统新增普通用户指南

文章目录 1 如何新增普通用户2 如何以普通用户权限运行服务3 普通用户如何访问root账户的ubus服务4 其他权限控制5 参考 Openwrt系统在默认情况下只提供一个 root账户,所有的服务都是以 root权限运行的,包括 WebUI也是通过root账户访问的,…

【C++航海王:追寻罗杰的编程之路】哈希的应用——位图 | 布隆过滤器

目录 1 -> 位图 1.1 -> 位图的概念 1.2 -> 位图的应用 2 -> 布隆过滤器 2.1 -> 布隆过滤器的提出 2.2 -> 布隆过滤器的概念 2.3 -> 布隆过滤器的插入 2.4 -> 布隆过滤器的查找 2.5 -> 布隆过滤器的删除 2.6 -> 布隆过滤器的优点 2.7…

视频监控汇聚平台LntonCVS视频集中存储平台解决负载均衡的方案

随着技术的进步和企业对监控需求的增加,视频监控系统规模不断扩大,接入大量设备已成常态化挑战。为应对这一挑战,视频汇聚系统LntonCVS视频融合平台凭借其卓越的高并发处理能力,为企业视频监控管理系统提供可靠的负载均衡服务保障…

6.Neo4j数据库备份

对neo4j数据进行备份、还原、迁移操作时,要关闭neo4j。 将neo4j作为服务使用进行安装: neo4j install-service 先执行上面的命令,才能执行 neo4j stop 数据备份 执行备份命令: neo4j-admin dump --databasegraph.db --to/ne…

C++的入门基础(二)

目录 引用的概念和定义引用的特性引用的使用const引用指针和引用的关系引用的实际作用inlinenullptr 引用的概念和定义 在语法上引用是给一个变量取别名,和这个变量共用同一块空间,并不会给引用开一块空间。 取别名就是一块空间有多个名字 类型& …

Docker基本管理1

Docker 概述 Docker是一个开源的应用容器引擎,基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具,是一种轻量级的“虚拟机”。 Docker 的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的、可移植的、自给自…

Spring Web MVC入门(2)(请求1)

目录 请求 1.传递单个参数 2.传递多个参数 3.传递对象 4.后端参数重命名(后端参数映射) 非必传参数设置 5.传递数组 请求 访问不同的路径就是发送不同的请求.在发送请求时,可能会带一些参数,所以学习Spring的请求,主要是学习如何传递参数到后端及后端如何接收. 1.传递单…

Linux多线程编程-哲学家就餐问题详解与实现(C语言)

在哲学家就餐问题中,假设有五位哲学家围坐在圆桌前,每位哲学家需要进行思考和进餐两种活动。他们的思考不需要任何资源,但进餐需要使用两根筷子(左右两侧各一根)。筷子是共享资源,哲学家们在进行进餐时需要…

IDEA中Git常用操作及Git存储原理

Git简介与使用 Intro Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. Git是一款分布式版本控制系统(VSC),是团队合作开发…

【pbootcms】新环境搭建环境安装时发生错误

【pbootcms】新环境搭建环境安装时发生错误 提示一下内容: 登录请求发生错误,您可按照如下方式排查: 1、试着删除根目录下runtime目录,刷新页面重试 2、检查系统会话文件存储目录是否具有写入权限; 3、检查服务器环境pathinfo及伪静态规则配置; 先按照…

Pygame开发五子棋之人机对战游戏

引言 Pygame是一个基于Python的开源游戏开发库,它包含了丰富的多媒体功能,尤其是针对游戏开发所需的各种组件。如果你对游戏开发感兴趣,但又不想从底层开始编写所有东西,Pygame可以成为一个理想的起点。本文将介绍Pygame的基本概…

javaScript的面试重点--预解析

目录 一.前言 二.预解析案例 一.前言 关于预解析,我们通过今天学习就能够知道解析器运行JS分为哪两步;能够说出变量提升的步骤和运行过程;能够说出函数提升的步骤和运行过程。 二.预解析案例 预解析,简而言之,也就是…

Gstreamer学习3.1------使用appsrc灌颜色信号数据

这个视频内容讲解的离散余弦变换,讲的很好, 离散余弦变换可视化讲解_哔哩哔哩_bilibili 其中讲到,把颜色变化转换为曲线的处理, 在前面的学习中,我们知道了可以向appsrc来灌数据来进行显示 Gstreamer学习3----灌数据…

昇思25天学习打卡营第21天|基于MindSpore的DCGAN生成漫画头像

基于MindSpore的DCGAN生成漫画头像 GAN基础原理 生成对抗网络(GAN)的基础原理是通过两个互相博弈的模型,生成模型和判别模型,来实现对数据分布的学习并产生新的、与真实数据极其相似的数据实例。 生成对抗网络(GAN&a…

SwiftUI 截图(snapshot)视频画面的极简方法

功能需求 在 万物皆可截图:SwiftUI 中任意视图(包括List和ScrollView)截图的通用实现 这篇博文中,我们实现了在 SwiftUI 中截图几乎任何视图的功能,不幸的是它对视频截图却无能为力。不过别着急,我们还有妙招。 在上面的演示图片中,我们在 SwiftUI 中可以随心所欲的截图…

【python数据结构精讲】双端队列

通过总结《流畅的Python》等书中的知识,总结Python中常用工具的方法。 deque,学名双端队列。 1. 常用方法 append():队列尾部添加appendleft():队首添加pop():移除队列最后一个元素popleft():移除队列第一…

Reinforced Causal Explainer for GNN论文笔记

论文:TPAMI 2023 图神经网络的强化因果解释器 论文代码地址:代码 目录 Abstract Introduction PRELIMINARIES Causal Attribution of a Holistic Subgraph​ individual causal effect (ICE)​ *Causal Screening of an Edge Sequence Reinforc…

springboot上传图片

前端的name的值必须要和后端的MultipartFile 形参名一致 存储本地

PDF公式转Latex

文章目录 摘要数据集 UniMER介绍下载链接 LaTeX-OCRUniMERNet安装UniMER 用的数据集介绍下载链接 PDF-Extract-Kit整体介绍效果展示评测指标布局检测公式检测公式识别 使用教程环境安装参考[模型下载](models/README.md)下载所需模型权重 在Windows上运行在macOS上运行运行提取…