当前位置: 首页 > 编程日记 > 正文

[二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义...

前言简介

class文件是源代码经过编译后的一种平台中立的格式
里面包含了虚拟机运行所需要的所有信息,相当于 JVM的机器语言
JVM全称是Java Virtual Machine  ,既然是虚拟机,他终归要运行在物理机上
在操作系统中体现出来的也就是一个进程
操作系统会给他分配资源,割一块内存作为他的地盘
class文件是静态的,想要运行程序,JVM需要将class文件中的信息加载到加载到他的地盘
然后处理他可以处理的数据类型的数据
JVM将这块内存按照功能进行了更细的划分,不过终究是一个规范,虚拟机的厂商在实现的时候仍旧有很大的自由度
接下来将会从两个方面    虚拟机可以处理的数据类型  以及    运行时的数据区的内存模型
对虚拟机进行简单的介绍

数据类型

数据类型分类

虚拟机可以处理的数据类型分为:基本类型和引用类型两类
所以对于值,也就存在基本类型值 和 引用值 两种类型的值
基本类型又分为 数值类型/boolean/returnAddress 三种
数值类型又分为整数类型和浮点数类型
整数类型(byte short int long char) 与浮点数(float  double)  与java语言中的值域在任何地方都是一致的,比如 取值范围表示含义
boolean编译后使用Java虚拟机中的int 数据类型代替,不过Java虚拟机支持boolean类型的数组,0表示false 1表示true
returnAddress 在Java语言中并不存在相应的类型 也就是程序员不能使用这个类型 ,而且也无法在程序运行期间更改
引用类型分为三种  类类型  接口类型 数组类型
值都是动态创建对象的引用
类类型的值是对类实例的引用
数组类型的值是对数组对象的引用
接口类型的值 是对实现了该接口的某个类实例的引用
另外还有一个特殊的引用null
image_5b84bfb7_6acf

取值范围

byte 8位   有符号 二进制补码整数  默认值零(-2^7到2^7-1  包括两端的值在内)
short 16位 有符号 二进制补码整数 默认值零(-2^15到2^15-1  包括两端的值在内)
int
32位 有符号 二进制补码整数 默认值零(-2^31到2^31-1  包括两端的值在内)
long
64位 有符号 二进制补码整数 默认值零(-2^64到2^64-1  包括两端的值在内)
char 16位 无符号 Unicode字符 默认值为null的码点 '\u0000'   (0 到2^16-1  包括两端的值在内)
float 32位 IEEE754标准单精度浮点数 默认值正数0
double 64位 IEEE754标准双精度浮点数 默认值正数0
returnAddress 同一方法中某操作码的地址
reference 堆中堆某对象的引用,或者是null



内存结构

内存结构组成部分

上面说过,程序运行,必然需要装载数据到内存
class文件会经由classLoader加载到JVM的运行时数据区域
JVM的内存结构为下图右侧部分
从图中可以看得出来
大致分为 方法区/堆/程序计数器/虚拟机栈/本地方法栈   五部分
接下来逐个进行介绍
ps:在抽象一点,逻辑上来说其实可以理解为堆/栈/程序计数器 三类 
程序的运行 , 需要数据还需要方法,还需要说明从哪个指令位置开始执行
程序计数器就是指向要执行的指令地址,标志从哪个位置开始执行
栈是方法调用概念的具体化数据结构,描述了怎么执行
堆用于保存程序运行需要用到的数据对象等,描述了 执行什么,操作什么
image_5b84bfb7_4329

内存结构各部分详情

一个运行时的java虚拟机实例就是负责一个java程序的运行
启动一个Java程序一个虚拟机实例也就诞生,当这个Java程序关闭,这个虚拟机实例就销毁 
每个Java程序都运行于他自己的Java虚拟机实例中
一个虚拟机实例中,堆和方法区是这个Java程序所有线程共享的
Java虚拟机栈和本地方法栈和程序计数器 是线程隔离独有的
下面所有说到的都是基于一个Java程序内的场景
image_5b84bfb8_34ac

方法区

方法区是可供各个线程共享的运行时内存区域,存储了每一个类的结构信息,如:
运行时常量池/字段和方法数据/构造函数和普通方法的字节码内容/类实例接口初始化时用到的特殊方法
方法区在虚拟机启动的时候创建
方法区也可以被垃圾收集
方法区大小不必是固定的 可以根据需要动态扩展  
方法区空间也不必是连续的
具体存储的信息包括:
类型信息
类的全限定名
类型的直接超类全限定名
类型 类还是接口
访问修饰符
直接超接口的全限定名
字段信息
字段名
字段类型
字段的修饰符
方法信息
方法名
方法的返回类型
方法的参数数量和类型
方法的修饰符
方法的字节码(有方法体的)
操作数栈和该方法栈帧中的局部变量表   的大小(其实也还是class文件属性表的内容 静态的)
常量池--下面的运行时常量池区域
除了常量以外的所有类变量
类变量是所有类实例共享的,即使没有任何类实例,他也可以被访问,这些变量仅仅和类有关
所以
类变量总是作为类型信息的一部分存储在方法区
除了在类中声明的编译时常量外,虚拟机使用某个类之前 必须在方法区中为这些类分配空间
编译时常量指的是final声明以及用编译时已知的值初始化的类变量
这种和一般的类变量还不一样,每个使用编译时常量的类型,都会复制他的所有常量到自己的常量池中 或者嵌入到他的字节码流中
说白了对于这种值不变的,直接复制过去
类ClassLoader的引用/Class类的引用
每个类被装载后都必须跟踪他是由哪个类加载器加载的
对于每个被装载的类型,不管是类还是接口,虚拟机都会相应的为他创建一个java.lang.Class类的实例
而且虚拟机还必须以某种方式把这个实例和存储在方法区中的类型数据关联起来

运行时常量池

运行时常量池属于方法区的一部分
class文件中每一个类或者接口的常量池表 constant_pool table 运行时的表示形式
只需要记住与class文件中的constant_pool相对应即可理解所包含的内容
包括了若干种不同的常量
从编译器可知的数值字面量到必须在运行期解析后才能获得的方法或字段引用
运行时常量在Java虚拟机的方法区分配  加载类或者接口到虚拟机后,就创建对应的运行时常量池
总结:
所有的类型信息,静态数据信息,都加载到方法区中
另外类加载器以及当前Class对象这种运行时必须的信息,也被保存在方法区

Java堆

一个java程序独占一个虚拟机实例,也就是每个java程序一个独立的堆空间
但是对于同一个java程序   堆是各个线程共享的运行时内存区域 
是所有类实例和数组对象分配内存的区域
Java堆在虚拟机启动时就被创建了
存储了被自动内存管理系统 也就是GC( garbage Collector) 所管理的各种对象
这些受管理的对象不需要也也不能显式的销毁
之所以这么说是因为有分配新对象的指令,却没有释放内存的指令,所以就不能显式的销毁
堆是垃圾收集器工作的主要区域 
堆空间不必连续也可以动态扩展或者收缩
对象的内部表示形式,规范并没有规定 实现者可以按需发挥


image_5b84bfb8_331e

Java虚拟机栈

java虚拟机栈,是对方法调用这一抽象概念的具体化描述,方法执行的内存模型
启动一个新线程 Java虚拟机就会为他分配一个Java栈,用于保存栈帧
虚拟机只会直接对Java栈执行两种操作   以栈帧为单位的出栈或者入栈
每个方法在执行的同时都会创建一个栈帧  栈帧用于存储局部变量表 操作数栈 动态链接 方法出口等信息
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程
栈上所有的数据都是线程私有的任何线程都不能访问另一个线程的栈数据
也就是说,完全无需考虑多线程情况下数据的访问同步问题
当一个线程调用另一个方法时,方法的局部变量保存在调用线程的Java虚拟机栈的栈帧中
只有一个线程总是能访问那些局部变量即调用方法的线程

栈帧

三部分组成:  局部变量表  操作数栈 以及栈帧数据区
当虚拟机调用一个方法时,从对应类的类型信息中得到此方法的局部变量表和操作数栈的大小(code 属性)
并以此分配栈帧内存,然后压入Java栈中
栈帧随着方法的调用而创建随着方法结束而销毁,无论方法正常完成还是异常完成都算作方法结束
局部变量表 长度由编译期决定,通过方法code属性提供 除了long和double 使用两个局部变量外,其余类型均为一个
保存了对应方法的参数和局部变量
操作数栈 后进先出,操作数栈最大深度编译期决定通过code属性保存提供 每个位置可以保存一个java虚拟机中定义的任意数据类型的值包括long double
操作数栈作为虚拟机的工作区,大多数指令都要从这里弹出数据执行计算然后把结果压回操作数栈
栈帧数据区 除了局部变量和操作数栈外,还需要一些其他的数据,比如 常量池的入口信息,每当虚拟机要执行某个需要用到常量池数据的指令时
都会通过栈帧数据区中指向常量池的指针来访问他

本地方法栈

本地方法栈并不是虚拟机明确定义的,是可选的
Java虚拟机实现可能会使用到传统的栈(通常称之为C stack) 来支持native方法(指 用Java以外的其他语言编写的方法)
这个栈就是本地方法栈 当Java虚拟机使用其他语言比如C语言,来实现指令集解释器的时候,也可以使用本地方法栈
如果Java虚拟机本身不支持native方法,或是本身不依赖传统栈,那么可以不提供本地方法栈
如果支持本地方法栈  那这个栈一般会在创建线程的时候按线程分配

程序计数器

程序计数器 又叫做 PC寄存器 PC为program counter
不管称呼如何,其本意是保存当前正在执行的指令的地址,
此处,我们可以看做是当前线程所执行的字节码的行号指示器
也就是程序的运行完全依赖PC寄存器,需要依靠他获取下一条需要执行的字节码指令
JVM的多线程时通过线程轮流切换并分配处理器执行时间片的方式实现的,在任何一个确定的时刻
一个处理器(一个内核) 都只能执行一条线程中的指令,为了线程切换后能恢复到正确的位置
所以每个线程都需要一个独立的程序计数器,所以程序计数器是线程私有的 线程启动时创建
如果执行的是Java方法,值为正在执行的虚拟机的字节码指令地址
如果是Native方法,计数器值为空 Undefined  ,此区域 没有OOM

直接内存

直接内存并不是虚拟机运行时的数据区,也不是Java虚拟机规范中定义的内存区
但是这部分内存也被频繁的调用,也可能导致OOM
是引入NIO后,引入的一种基于通道与缓冲区的IO方式
可以使用native 函数库直接分配堆外内存,然后通过Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
能在一些场景中显著提高性能
既然不属于java堆,自然不受制于Java堆大小的限制,但是,必须运行于物理机
自然受制于本机总内存大小

总结

JVM运行时的内存结构,就是为了执行字节码文件,而将class文件中的信息加载到内存中的一个逻辑映射
class文件是源代码的静态抽象的数据结构描述
运行时内存结构是对于class文件的执行行为的结构描述
以上所有的要求说明都是属于规范上的并不要求所有的实现与规范中定义的抽象元素完全的对应起来
抽象的内部组件和行为的描述,仅仅是定义Java虚拟机所应该呈现出来的外部行为
也就是说,一个具体的虚拟机实现,可能与我们说过的规范相同,也可能与规范有出入
但是只要他的外部行为是一致的,正确识别class文件,遵守class文件中包含的Java代码的语义,能够按照规定所需要呈现出来的行为结果
执行字节码文件即可
至于方法区到底应该如何分配空间,对象的内部表现形式如何,垃圾收集器如何运作,如何加载类都是由设计者来决定实现的.
举一个浅显的例子
你去超市购物,为了方便携带,你可能会按照他们的形状或者类别组织放到购物袋里面
比如 生鲜放到一个袋子,零食放到一个袋子,或者放置稍大商品的购物袋里面的缝隙处,放置一些小的商品
这是属于所有商品的静态描述组织
回到家需要做饭,可能你会把鱼拿出来放到盘子里,可能你会把青菜放到水槽中浸泡清洗,然后你可能会准备作料,洗锅准备做菜等等
一切都按照你下厨的习惯来放置食材以及步骤进行做菜
这就是属于动态执行行为的结构描述
我们的内存结构 程序计数器  堆   栈 就是对于代码执行行为过程的一种描述
可以理解你想要先做那道菜? 程序计数器((如果把想要做的菜都列一个清单,程序计数器就是从什么位置开始做,就是先做哪道菜)
都有哪些食材? 台面上有青菜 鱼 豆腐...  这都是存放在堆中
具体的怎么做? 红烧还是清蒸?这些具体的行为封装在虚拟机栈的栈帧中  每次做一道菜就是入栈,做好了刷锅就是出栈
而每道菜所需要的调味料和配菜可能是独有的,不能乱放,这些就相当于栈帧中的局部变量和操作数栈

相关文章:

import android.support.v7.widget.RecyclerView失败

换成 androidx.recyclerview.widget.RecyclerView 参考文章 https://blog.csdn.net/u013183608/article/details/89428611/

CrackMe_001

本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注册机的东西。 其中,文章中按照如下逻辑编排(解决如下问题)&…

用javascript实现的纵版飞行射击游戏—《天机》

花了一个半月的时间用javascript完成了这款web版飞行射击游戏,游戏效果接近一般的客户端游戏,不过对机器的要求稍微高点点,主要是CPU,最好在1.5GHZ以上,不然可能会比较卡,支持IE、FF、Opera、safari。 用ja…

对分组交换(packet switching)高效迅速灵活可靠四个优点的理解

1.什么是分组? 通信过程中要发送的整块数据被称为一个报文(message),报文被划分为一个个更小的等长数据段,每个数据段前加入一些由必要的控制信息组成的首部后,就构成了一个分组。分组是在互联网中传送的数据单元(长报文&#xff…

06、ActivationDeactivation

1、将App.xaml中的StartupUri"MainWindow.xaml"删除。 2、使用NuGet安装Prism.Wpf、Prism.Core、Prism.Unity。 3、添加类“Bootstrapper”,编辑如下: 1 using System;2 using System.Collections.Generic;3 using System.Linq;4 using System…

Git 学习笔记一

Git的基本配置和使用 一、git add ;git commit;git commit -a(默认跟踪修改直接提交(不包括新文件))。 二、tig命令 查看修改记录的前端工具,方面查看修改记录。相当于git log –p。 三、git config --global alias.ci "commit -a -v"添加命令别名&#x…

vb 取得计算机名及目录

Public gCompName 取得计算机名及Windows目录 Dim i% Dim c$ Dim cSql As String Dim cProduct As String c Space(256) i GetComputerName(c, 256) gCompName Trim(c) gCompName Left(gCompName, Len(gCompName) - 1) 读取MAC地址 Dim…

速率单位和信息量单位区分

网络技术钟的速率指的是数据的传送速率,也称为数据率或比特率。 单位是bit/s 比特每秒 也写作b/s 或bps(bit per second) 当数据率较高时 常常在bit/s前面加一个字母,如 k 10^3 M 10^6 G 10^9 T 10^12 P 10^15 …… 数据量往往用字节B作为度量单位…

python 自动生成C++代码 (代码生成器)

python 代码自动生成的方法 (代码生成器) 遇到的问题 工作中遇到这么一个事,需要写很多C的底层数据库类,但这些类大同小异,无非是增删改查,如果人工来写代码,既费力又容易出错;而借用…

WPF实用指南二:移除窗体的图标

原文:WPF实用指南二:移除窗体的图标WPF没有提供任何功能来移除窗体上的icon图标。一般的做法是设置一个空白的图标,如下图1: 这种做法在窗体边框与标题之间仍然会保留一片空白。比较好的做法是使用Win32API提供的函数来移除这个图标。使用如下的代码&…

什么是EAI?

什么是EAI(enterprise application integration)企业应用集成? EAI是将基于各种不同平台、用不同方案建立的异构应用集成的一种方法和技术。EAI通过建立底层结构,来联系横贯整个企业的异构系统、应用、数据 源等,完成在企业内部的 ERP、CRM、SCM、数据库…

C# 中的委托和事件

引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉…

零代价修复海量服务器的内核缺陷——UCloud内核热补丁技术揭秘

下述为UCloud资深工程师邱模炯在InfoQ架构师峰会上的演讲——《UCloud云平台的内核实践》中非常受关注的内核热补丁技术的一部分。给大家揭开了UCloud云平台内核技术的神秘面纱。 如何零代价修复海量服务器的Linux内核缺陷? 对于一个拥有成千上万台服务器的公司&…

软件工程技术基础-(软件复用技术)

软件可重用问题,包括源程序代码重用、静态库重用和组建重用。 源程序代码重用是直接将其他项目或系统开发完成的代码复制过来,直接使用。 限制源程序代码重用技术使用的关键因素是要考虑代码的语言实现,以及源代码 公开可能带来的知识产权问题…

Parcelable与Serializable的比较

Parcel: Android中的序列化方式,可用于跨进程传输 Parcelable 进程间 如:想从一个第三方app拿进程回来 Serializable 进程内

20140725 快速排序时间复杂度 sTL入门

1、快速排序的时间复杂度(平均时间复杂度为) 数组本身就有序时&#xff0c;效果很差为O(n^2) 2、STl入门 &#xff08;1&#xff09; C内联函数(inline)和C中宏(#define)区别 内联函数有类型检查&#xff0c;宏定义没有&#xff1b;C编程尽量使用内联函数 template <class T…

小编带你进入强如 Disruptor 也发生内存溢出?

前言OutOfMemoryError 问题相信很多朋友都遇到过&#xff0c;相对于常见的业务异常&#xff08;数组越界、空指针等&#xff09;来说这类问题是很难定位和解决的。 本文以最近碰到的一次线上内存溢出的定位、解决问题的方式展开&#xff1b;希望能对碰到类似问题的同学带来思路…

数据库反规范设计

< DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd> 反规范化设计 为了提升性能而使用反规范化设计 常用方法&#xff1a; A、在多个表中存储某个字段的副本 B、在父表中存储汇总值 C、将活动数据和历史数据分开存储 D、应用程序本地缓…

安卓的两种界面编写方式对比

1.XML进行描述 优点是可以直接在Android studio Preview 栏中查看效果(所见即所得&#xff0c;但是不是所有的都可以立刻看到效果) 注意&#xff1a;包含两种方式-编辑layout文件夹下的XML文件 和 直接从下图的图形化界面操作 2.Java/Kotlin代码进行编写 随着学习的深入&#x…

对象***已断开连接或不在该服务器上 的解决方案之一

使用VS2008在发布网站的时候&#xff0c;出现了这样的一个错误&#xff0c;先前一直是OK的。网上找了老半天&#xff0c;几乎没有此问题的解决办法。很是郁闷。只能一个一个地进行编译。单个层Build是OK的&#xff0c;整个Solution的Rebuild也是OK的&#xff0c;一开始使用VS自…

Web.XML文件中关于代码提示的一些问题

1. 问题描述 在进行ssm的整合时&#xff0c;我发现在web.xml文件中里按了alt/以后没有下面的提示&#xff1a;这样真的很不爽。。。于是弄了半天&#xff08;主要是等着&#xff09;终于解决了&#xff0c;特此写篇博客记录下。 2. 解决方案 在eclipse中安装Spring IDE的插件&a…

安卓开发之点九图

Nine-Patch图 xxx.9.png 口诀&#xff1a;左上进行拉伸&#xff0c;右下进行显示。

ImportError: No module named images

&#xff3b;问题&#xff3d; 在使用学习wxPython时&#xff0c;一个Dem抱有如题所示错误 &#xff3b;解决&#xff3d; images 只不过是wxpython自带demo中的一个文件 体验wxpython IN action的时候ImportError: No module named images替换为import wx.py.images as images…

从 Java 到 Scala(二):object

本文由 Rhyme 发表在 ScalaCool 团队博客。 object是一种让静态回归常态、打破模式、天然的语言特性。 其实在写这篇文章之前&#xff0c;我思绪万千&#xff0c;迟迟不能落笔&#xff0c;总想着自己会不会遗漏了某个知识点&#xff0c;或者有讲得不太那么准确的地方&#xff0…

Python获取屏幕分辨率大小

获取屏幕大小有两种方法可以办到: 1.wxPython里的 2.win32api 1 #coding:gb23122 #wxApp.py 3 #author: aoogur4 importos5 importwx6 fromwin32api importGetSystemMetrics7 8 classFrame(wx.Frame):9 def__init__(self):10 wx.Frame.__init__(self,None,-1,title"wxApp.…

安卓事件传递机制

1.触摸事件 MotionEvent ACTION_DOWN:按下 ACTION_MOVE:移动 ACTION_UP:松开 2.以上三个触摸事件都会经历三个函数 事件分发&#xff08;Dispatch&#xff09;&#xff1a;dispatchTouchEvent 事件拦截 &#xff08;Intercept&#xff09;&#xff1a;onInterceptTouchEvent 事…

财务软件的管理监督

随着现代企业的发展&#xff0c;企业财务管理的内涵、外延、功能及其地位发生了深刻的变化&#xff0c;强化企业的财务管理已经成为现代企业在激烈的市场竞争中得以生存和发展、现代企业制度得以保证和实施的重要环节。财务管理软件的应用已经非常普及了。 现代企业财务管…

[Vue CLI 3] 插件编写实战和源码分析

当你看过了官方的几个插件之后&#xff0c;慢慢地&#xff0c;其实你也有需求了。 那如何编写一个 Vue CLI 3 的插件呢&#xff1f;本文代码已经放到 github 上&#xff0c;地址&#xff1a;https://github.com/dailynodej... 我们建一个文件夹&#xff0c;取名 vue-cli-plugin…

突然想起99年的那次离别

今天妹妹离开成都回家了突然发现某一件事情又那么蹿了出来了&#xff0c;是99年的秋天那次&#xff0c;同f到成都且分开的那短暂的2天&#xff01;很多年了&#xff0c;不知道是忘记了再回忆起&#xff0c;还是一直埋在最深处&#xff0c;瞬间又重现&#xff01;想了&#xff0…