虚拟机能够处理的数据类型分为:基本类型和引用类型两类
因此对于值,也就存在基本类型值 和 引用值 两种类型的值
|
基本类型又分为 数值类型/boolean/returnAddress 三种
数值类型又分为整数类型和浮点数类型
整数类型(byte short int long char) 与浮点数(float double) 与java语言中的值域在任何地方都是一致的,好比 取值范围表示含义
boolean编译后使用Java虚拟机中的int 数据类型代替,不过Java虚拟机支持boolean类型的数组,0表示false 1表示true
returnAddress 在Java语言中并不存在相应的类型 也就是程序员不能使用这个类型 ,并且也没法在程序运行期间更改
|
引用类型分为三种 类类型 接口类型 数组类型 值都是动态建立对象的引用 类类型的值是对类实例的引用 数组类型的值是对数组对象的引用 接口类型的值 是对实现了该接口的某个类实例的引用 另外还有一个特殊的引用null |
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 |
ps:在抽象一点,逻辑上来讲其实能够理解为堆/栈/程序计数器 三类
程序的运行 , 须要数据还须要方法,还须要说明从哪一个指令位置开始执行
程序计数器就是指向要执行的指令地址,标志从哪一个位置开始执行
栈是方法调用概念的具体化数据结构,描述了怎么执行
堆用于保存程序运行须要用到的数据对象等,描述了 执行什么,操做什么
|
方法区是可供各个线程共享的运行时内存区域,存储了每个类的结构信息,如:
运行时常量池/字段和方法数据/构造函数和普通方法的字节码内容/类实例接口初始化时用到的特殊方法
方法区在虚拟机启动的时候建立
方法区也能够被垃圾收集
方法区大小没必要是固定的 能够根据须要动态扩展
方法区空间也没必要是连续的
具体存储的信息包括:
|
类型信息
类的全限定名
类型的直接超类全限定名
类型 类仍是接口
访问修饰符
直接超接口的全限定名
|
字段信息 字段名 字段类型 字段的修饰符 |
方法信息 方法名 方法的返回类型 方法的参数数量和类型 方法的修饰符 方法的字节码(有方法体的) 操做数栈和该方法栈帧中的局部变量表 的大小(其实也仍是class文件属性表的内容 静态的) |
常量池--下面的运行时常量池区域 |
除了常量之外的全部类变量 类变量是全部类实例共享的,即便没有任何类实例,他也能够被访问,这些变量仅仅和类有关 因此 类变量老是做为类型信息的一部分存储在方法区 除了在类中声明的编译时常量外,虚拟机使用某个类以前 必须在方法区中为这些类分配空间 编译时常量指的是final声明以及用编译时已知的值初始化的类变量 这种和通常的类变量还不同,每一个使用编译时常量的类型,都会复制他的全部常量到本身的常量池中 或者嵌入到他的字节码流中 说白了对于这种值不变的,直接复制过去 |
类ClassLoader的引用/Class类的引用 每一个类被装载后都必须跟踪他是由哪一个类加载器加载的 对于每一个被装载的类型,不论是类仍是接口,虚拟机都会相应的为他建立一个java.lang.Class类的实例 并且虚拟机还必须以某种方式把这个实例和存储在方法区中的类型数据关联起来 |
运行时常量池属于方法区的一部分
class文件中每个类或者接口的常量池表 constant_pool table 运行时的表示形式
只须要记住与class文件中的constant_pool相对应便可理解所包含的内容
包括了若干种不一样的常量
从编译器可知的数值字面量到必须在运行期解析后才能得到的方法或字段引用
运行时常量在Java虚拟机的方法区分配 加载类或者接口到虚拟机后,就建立对应的运行时常量池
|
一个java程序独占一个虚拟机实例,也就是每一个java程序一个独立的堆空间
可是对于同一个java程序 堆是各个线程共享的运行时内存区域
是全部类实例和数组对象分配内存的区域
Java堆在虚拟机启动时就被建立了
存储了被自动内存管理系统 也就是GC( garbage Collector) 所管理的各类对象
这些受管理的对象不须要也也不能显式的销毁
之因此这么说是由于有分配新对象的指令,却没有释放内存的指令,因此就不能显式的销毁
堆是垃圾收集器工做的主要区域
堆空间没必要连续也能够动态扩展或者收缩
对象的内部表示形式,规范并无规定 实现者能够按需发挥
|
java虚拟机栈,是对方法调用这一抽象概念的具体化描述,方法执行的内存模型
启动一个新线程 Java虚拟机就会为他分配一个Java栈,用于保存栈帧
虚拟机只会直接对Java栈执行两种操做 以栈帧为单位的出栈或者入栈
每一个方法在执行的同时都会建立一个栈帧 栈帧用于存储局部变量表 操做数栈 动态连接 方法出口等信息
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程
栈上全部的数据都是线程私有的任何线程都不能访问另外一个线程的栈数据
也就是说,彻底无需考虑多线程状况下数据的访问同步问题
当一个线程调用另外一个方法时,方法的局部变量保存在调用线程的Java虚拟机栈的栈帧中
只有一个线程老是能访问那些局部变量即调用方法的线程
|
三部分组成: 局部变量表 操做数栈 以及栈帧数据区
当虚拟机调用一个方法时,从对应类的类型信息中获得此方法的局部变量表和操做数栈的大小(code 属性)
并以此分配栈帧内存,而后压入Java栈中
栈帧随着方法的调用而建立随着方法结束而销毁,不管方法正常完成仍是异常完成都算做方法结束
局部变量表 长度由编译期决定,经过方法code属性提供 除了long和double 使用两个局部变量外,其他类型均为一个
保存了对应方法的参数和局部变量
操做数栈 后进先出,操做数栈最大深度编译期决定经过code属性保存提供 每一个位置能够保存一个java虚拟机中定义的任意数据类型的值包括long double
操做数栈做为虚拟机的工做区,大多数指令都要从这里弹出数据执行计算而后把结果压回操做数栈
栈帧数据区 除了局部变量和操做数栈外,还须要一些其余的数据,好比 常量池的入口信息,每当虚拟机要执行某个须要用到常量池数据的指令时
都会经过栈帧数据区中指向常量池的指针来访问他
|
本地方法栈并非虚拟机明肯定义的,是可选的
Java虚拟机实现可能会使用到传统的栈(一般称之为C stack) 来支持native方法(指 用Java之外的其余语言编写的方法)
这个栈就是本地方法栈 当Java虚拟机使用其余语言好比C语言,来实现指令集解释器的时候,也可使用本地方法栈
若是Java虚拟机自己不支持native方法,或是自己不依赖传统栈,那么能够不提供本地方法栈
若是支持本地方法栈 那这个栈通常会在建立线程的时候按线程分配
|
程序计数器 java
程序计数器 又叫作 PC寄存器 PC为program counter
无论称呼如何,其本意是保存当前正在执行的指令的地址,
此处,咱们能够看作是当前线程所执行的字节码的行号指示器
也就是程序的运行彻底依赖PC寄存器,须要依靠他获取下一条须要执行的字节码指令
JVM的多线程时经过线程轮流切换并分配处理器执行时间片的方式实现的,在任何一个肯定的时刻
一个处理器(一个内核) 都只能执行一条线程中的指令,为了线程切换后能恢复到正确的位置
因此每一个线程都须要一个独立的程序计数器,因此程序计数器是线程私有的 线程启动时建立
若是执行的是Java方法,值为正在执行的虚拟机的字节码指令地址
若是是Native方法,计数器值为空 Undefined ,此区域 没有OOM
|
直接内存并非虚拟机运行时的数据区,也不是Java虚拟机规范中定义的内存区
可是这部份内存也被频繁的调用,也可能致使OOM
是引入NIO后,引入的一种基于通道与缓冲区的IO方式
可使用native 函数库直接分配堆外内存,而后经过Java堆中的DirectByteBuffer对象做为这块内存的引用进行操做
能在一些场景中显著提升性能
既然不属于java堆,天然不受制于Java堆大小的限制,可是,必须运行于物理机
天然受制于本机总内存大小
|
举一个浅显的例子
你去超市购物,为了方便携带,你可能会按照他们的形状或者类别组织放到购物袋里面
好比 生鲜放到一个袋子,零食放到一个袋子,或者放置稍大商品的购物袋里面的缝隙处,放置一些小的商品
这是属于全部商品的静态描述组织
回到家须要作饭,可能你会把鱼拿出来放到盘子里,可能你会把青菜放到水槽中浸泡清洗,而后你可能会准备做料,洗锅准备作菜等等
一切都按照你下厨的习惯来放置食材以及步骤进行作菜
这就是属于动态执行行为的结构描述
咱们的内存结构 程序计数器 堆 栈 就是对于代码执行行为过程的一种描述
能够理解你想要先作那道菜? 程序计数器((若是把想要作的菜都列一个清单,程序计数器就是从什么位置开始作,就是先作哪道菜)
都有哪些食材? 台面上有青菜 鱼 豆腐... 这都是存放在堆中
具体的怎么作? 红烧仍是清蒸?这些具体的行为封装在虚拟机栈的栈帧中 每次作一道菜就是入栈,作好了刷锅就是出栈
而每道菜所须要的调味料和配菜多是独有的,不能乱放,这些就至关于栈帧中的局部变量和操做数栈
|