【JVM】JVM五大内存区域介绍

目录

 一、程序计数器(线程私有)

二、java虚拟机栈(线程私有)

2.1、虚拟机栈

2.2、栈相关测试

2.2.1、栈溢出

三、本地方法栈(线程私有)

四、java堆(线程共享)

五、方法区(线程共享)

六、实例演示


        Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。

 一、程序计数器(线程私有)

        程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里 [1] ,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
        由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

二、java虚拟机栈(线程私有)

2.1、虚拟机栈

        与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧 [1] (Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

        “栈”通常就是指这里讲的虚拟机栈,或者更多的情况下只是指虚拟机栈中局部变量表部分。
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用
(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
        这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。请读者注意,这里说的“大小”是指变量槽的数量,虚拟机真正使用多大的内存空间(譬如按照1个变量槽占用32个比特、64个比特,或者更多)来实现一个变量槽,这是完全由具体的虚拟机实现自行决定的事情。

2.2、栈相关测试

2.2.1、栈溢出

测试类:

如果某个线程的线程栈的内存被耗尽,没有足够的内存资源去创建栈帧,就会发生内存溢出。

public class TestStack {
    public static void main(String[] args) {
        test();
    }
    public static void test(){
        test();
    }
}

结果:

        上面这串代码的执行过程是:线程先执行main方法,同时会创建main方法的栈帧插入到该线程的线程栈中,当执行到test()方法时,创建test()方法的栈帧插入到该线程的线程栈中,执行到test()方法里的test()方法时,创建栈帧,插入到线程栈中,后面进行不断创建栈帧、入栈。当创建一定数量的栈帧后,剩下的线程资源无法再创建新的栈帧就会报StackOverflowError异常(堆栈溢出异常)(当前虚拟机栈不可以动态扩展)

        如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。

三、本地方法栈(线程私有)

        本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

四、java堆(线程共享)

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

        Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”(Garbage CollectedHeap)。从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现“新生代”“老年代”“永久代”“Eden空间”“From Survivor空间”“To Survivor空间”等名词。

        如果从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区
(Thread Local Allocation Buffer,TLAB)
,以提升对象分配时的效率。不过无论从什么角度,无论如何划分,都不会改变Java堆中存储内容的共性,无论是哪个区域,存储的都只能是对象的实例,将Java堆细分的目的只是为了更好地回收内存,或者更快地分配内存。

        Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

五、方法区(线程共享)

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

        如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。

六、实例演示

1、基本数据类型

实体类

@Data
public class UserParam {

    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String password;
    /**
     * 昵称
     */
    private String nickName;

    /**
     * 旧密码
     */
    private String oldPassWord;

    /**
     * 新密码
     */
    private String newPassWord;

}

测试

public class TestStack {
    public static void main(String[] args) {
        UserParam userParam=new UserParam();
        int b=1;
        test(userParam,b);
        test(userParam,b);
        System.out.println(userParam);
    }
    public static void test(UserParam userParam,int a){
        userParam.setUserName(String.valueOf(a));
        a=2;
    }
}

结果:

分析:

b变量是基本数据类型,创建完在栈帧中,没有任何引用,参数也只是传入值,方法中a改变对b没有任何影响。

 2、引用数据类型会根据传入的引用数据的改变而改变

public class TestStack {
    public static void main(String[] args) {
        UserParam userParam=new UserParam();
        userParam.setNickName("张三");
        test(userParam);
        test(userParam);
        System.out.println(userParam);
    }
    public static void test(UserParam userParam){
        userParam.setUserName(userParam.getNickName());
        userParam.setNickName("李四");
    }
}

结果:

分析:

将对象作为参数传入时,test方法中的对象也是指向同一片内存区域,操作的是同一块内存,所以在test方法中改变属性的值会影响外边同一个对象内的属性值。

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

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

相关文章

用i18next使你的应用国际化-React

ref: https://www.i18next.com/ i18next是一个用JavaScript编写的国际化框架。 i18next为您提供了一个完整的解决方案,本地化您的产品从web端到移动端和桌面端。 在react项目中安i18next依赖: i18nextreact-i18nexti18next-browser-languagedetector&…

python学习时与chatgpt4对话的一些感悟

今天学SCENIC教程,看到里面有一句不是很懂 If you run this from a python script instead of a Jupyter notebook, please enclose the code in a if __name__ __main__: construct. 现在把和chatgpt4问答的内容发上来,确实是很厉害 没有太看懂&…

QT中日期和时间类

QT中日期和时间类 QDateQTimeQDateTime QDate QDate类可以封装日期信息也可以通过这个类得到日期相关的信息, 包括:年, 月, 日。 // 构造函数 QDate::QDate(); QDate::QDate(int y, int m, int d);// 公共成员函数 // 重新设置日期对象中的日期 bool QDate::setDate(int year…

数据结构---LRU CACHE

什么是LRU 通过之前的学习我们知道计算机在处理任务的时候是先将数据从硬盘中提取出来加载进内存,然后再将内存中的数据加载进入cpu进行计算,但是这里存在一个问题cpu的计算速度非常快,而内存中加载数据的速度又很慢,所以为了提供…

【数据仓库】Apache Hive初体验

为什么使用Hive? 使用Hadoop MapReduce直接处理数据所面临的问题: 人员学习成本太高需要掌握ava语言MapReduce实现,复杂查询逻辑开发难度太大! 1,使用Hive处理数据的好处操作接口采用类SQL语法,提供快速开发…

Vue学习Day3——生命周期\组件化

一、Vue生命周期 Vue生命周期:就是一个Vue实例从创建 到 销毁 的整个过程。 生命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 销毁 1.创建阶段:创建响应式数据 2.挂载阶段:渲染模板 3.更新阶段:修改数据,更…

C#多线程

C#多线程 C#多线程是C#学习中必不可少的知识,在实际开发中也能有效的提升用户体验,和程序性能。 文章目录 C#多线程前言一、什么是线程、什么是进程、什么是协程?协程优点缺点 线程优点缺点: 进程优点缺点: 二、C# 中…

FANUC机器人SRVO-217故障报警原因分析及参考解决办法

FANUC机器人SRVO-217故障报警原因分析及参考解决办法 如下图所示,示教器提示:SRVO-217紧急停止电路板未找到, 查阅手册可以看到以下的报警说明: 故障原因: 通电时未能识别紧急停止电路板或者增设的安全I/O装置。连接有多个安全I/O装置的系统中,在报警信息的最后,会显示发…

Redis实战(3)——缓存模型与缓存更新策略

1 什么是缓存? 缓存就是数据交换的缓冲区, 是存贮数据的临时区,一般读写性能较高 \textcolor{red}{是存贮数据的临时区,一般读写性能较高} 是存贮数据的临时区,一般读写性能较高。缓存可在多个场景下使用 以一次 w e b 请求为例…

如何少走弯路?蚓链助力零售企业实现数字化转型

基于大环境下的数据驱动,创新业务模式成为了后疫情时代下零售企业冲破困局、拓展业务的必然趋势,新零售概念应运而生。新零售结合数字化应用技术为传统零售企业打造线上营销生态链,帮助企业积累数据,盘活数据实现更大营收价值。 …

微信读书:长期投资(阅读摘录)

微信读书:长期投资(阅读摘录) 所有投资高手的时间精力都投向了这三大块:行动、思考、读书。 我们把耐心发挥到了极致,这正是价值投资的关键特征之一。 通常在牛市中想要跑赢大盘,难度非常大。 实际上&am…

QTday2信号和槽

点击登录按钮,关闭Widget登录窗口,打开QQList窗口 widget.cpp #include "widget.h"void my_setupUI(Widget *w);Widget::Widget(QWidget *parent): QWidget(parent) {my_setupUI(this); }Widget::~Widget() { }void Widget::login_slots() {//fixemit jump_signal(…

飞行动力学 - 第15节-part 1-操纵力与铰链力矩 之 基础点摘要

飞行动力学 - 第15节-part 1-操纵力与铰链力矩 之 基础点摘要 1. HOTAS全拼2. 操纵杆力&铰链力矩3. 铰链力矩4. 气动补偿(Aerodynamic Balancing)5. 参考资料 1. HOTAS全拼 Hands On Throttle And Stick 2. 操纵杆力&铰链力矩 操纵杆力&#…

Vue2 第三节 数据代理和事件处理

1.Object.defineProperty 方法 2.数据代理 3.Vue中的数据代理 4.事件的基本使用 5.事件修饰符 6.键盘事件 一.Object.defineProperty 方法 (1)学习Object.defineProperty为下一节数据代理做准备 (2)更加高级的给对象添加属…

【LeetCode】383. 赎金信

题目:383. 赎金信 由于此题只含有小写字母,并且magazine里面的字母不可重复使用. 故首先用一个长度为26的整形数组记录magazine里字母出现的次数。 再用这个整形数组跟ransomeNote进行遍历比较,当数组中出现-1时,说明false,否则true. 代码&am…

智慧园区安保人员巡更巡检解决方案,蓝牙信标主动式蓝牙定位导航系统

一、需求分析 目前,大部分写字楼,工厂,学校,银行,车站等场景对安保人员的管理依然靠手填单子记录作业情况,在缺乏信息化手段的情况下,靠人员自觉性或者RFID巡更棒,在这些传统方式下…

el-table数据处理

在写表格时遇到,后端返回的数据是对象,并且缺少字段 1.每一条数据加上 一个字段 2.将对象转成数组 以下是数据 {"groupA": {"groupName": null,"orgName": null,"orgId": null,"allPeoper": &quo…

libcomposite: Unknown symbol config_group_init (err 0)

加载libcomposite.ko 失败 问题描述 如图,在做USB OTG 设备模式的时候需要用到libcomposite.ko驱动,加载失败了。 原因&解决方法 有一个依赖叫configfs.ko的驱动没有安装。可以从内核代码的fs/configfs/configfs.ko中找到这个驱动。先加载confi…

从零开始理解Linux中断架构(23)中断运行临界区和占先调度

Linux在内核中定义了6种运行临界区。 in_interrupt in_interrupt在驱动中使用频率最高的函数了,in_interrupt()就是指示Core是否正在中断处理中,包含了硬中断,软中断运行临界区。如果在中断处理中,则不能调用__do_softirq执行软中断处理。硬中断中不可调度不可中断,所有…

【NLP】温和解读:transformer的核心思想

变压器模型及其关键组件的概述。 一、介绍 在这篇博文中,我将讨论本世纪最具革命性的论文“注意力是你所需要的一切”(Vaswani et al.)。首先,我将介绍自我注意机制,然后介绍变形金刚的架构细节。在之前的博客文章《从…