jvm:RunTimeDataArea

​ java虚拟机规范规定了在程序执行阶段使用的各种运行时数据区,有的随着虚拟机进程的启动而出创建、退出而销毁,有的随着用户线程的创建而创建、退出而销毁。
在这里插入图片描述

PC Register

​ 程序计数器(Program Computer Register),是一块较小的内存空间。如果执行的不是本地方法,那么存储的是当前正在执行指令的字节码偏移号,如果执行的是本地方法,那么计数器存储的就是空(Undefined)。因为jvm虚拟机是支持多线程执行的,那么在进行线程切换的时候为了保证每个线程都知道自己执行到那个地方了,所以程序计数器是每个线程一份。
​ 程序计数器只是存储指令的偏移号,所需的内存很小。因此在jvm虚拟机规范中是唯一一个没有规定OutOfMemory 的区域。

JVM Stack

​ Java虚拟机栈(Java Visual Machine Stack)也是线程私有的。存储的是线程中每个方法执行的内存模型,当执行一个方法的时候,JVM stack就会创建一个栈帧(Stack Frame)。栈帧中包含本地变量和部分结果,并扮演方法调用和返回部分。方法执行的时候栈帧进入JVM stack,执行结束栈帧出栈。
​ java虚拟机规范规定了该区域可以是固定容量大小,或者是动态扩展容量。
​ 当容量是动态扩展的时候可能会出现OutOfMemoryError异常(动态扩展时无法申请到足够的内存或线程创建的时候无法申请到足够内存创建栈),当容量是固定大小的时候则有可能出现StackOverflowError异常(栈帧的深度超过了我们使栈的容量。
​ 我们一般使用的hotspot虚拟机的栈是不可以动态扩展的,但任然有可能因为创建线程申请栈空间不足而报出OOM异常

Native Methods Stack

​ 本地方法栈(Native Methods Stack)存与虚拟机栈承担的作用基本是一样的,也都是线程私有的。不过本地方法栈中存放的并不是java语言写的方法。在虚拟机规范中并没有规定本地方法栈中使用的语言和数据结构,在java虚拟机中是一般调用的本地C/C++的库。在看java的一些源码中有的方法没有方法体但是有一个native修饰的就是本地方法。
​ 与虚拟机栈相同的是虚拟机规范也规定了该区域可以是固定大小或动态扩展的。
​ 我们使用的hotspot虚拟机将本地方法栈和虚拟栈做到了一起,所以设置本地方法栈的虚拟机栈是没有作用的。

Java Heap

​ java堆是虚拟机启动的时候创建的,所有的线程共享一个堆空间。几乎所有的java对象和数组存储在堆空间中(ps:因为现在有栈上分配,标量替换等手段),字符串常量池也存在java堆中。
​ 为了保证堆空间的容量的可用,堆空间要频繁的进行GC操作,因为java堆也被称为Gc堆。目前常见虚拟机的堆设计都是基于分代的思想设计的,分为新生代和老年代,新生代又分为edan区,servious1区,servious2区。分代的目的单纯的是为方便垃圾回收,新生代存放刚创建的对象这个区域垃圾回收比较频繁,在老年代存放长时间使用的对象或是新生代Gc后任然放不下的对象这个区域回收频率比较低。(ps:jvm虚拟机规范并没有规定堆一定要用分代设计,所以将堆设计等同于分代是不对的)。
虚拟机规范中规定堆在物理上可以是不连续的,但是在逻辑上一定要视为连续
​ java堆的容量既可以是固定的,也可以是可扩展的。目前主流的都是按照可扩展设计的(-Xms和-Xmx设置最小堆和最大堆容量)。
​ 当堆中没有办法再分配新的内存给实例的时候就会报OutOfMemoryError异常。

Method Area

​ 方法区是在虚拟机启动的时候创建的,所有的线程共享一个方法区。 方法区用来存放静态变量,加载的类型信息,常量,JIT编译后的代码缓存等信息。
​ 虽然在jvm虚拟中,将方法区逻辑上划分为是堆的一部分,但是他还有一个别名非堆(non heap)。在hostSpot虚拟机中,jdk8以前实现的方式为“永久代”,jdk8以后为"元空间"(直接使用本地的内存的实现方法)。
​ 在虚拟机规范中方法区可以在物理上不连续,可以固定和扩展大小,甚至可选择不实现垃圾回收。
​ 如果方法区域中的内存不能满足分配请求,Java虚拟机抛出一个OutOfMemoryError

Runtime Constant Pool

​ 运行时常量池是方法区的一部分。常量池表是Class文件的一部分,用于存放编译时期生成的各种字面量和符号引用,这部分内容加载后就存放在运行时常量池中。
​ JVM为每个已经加载的类型(类和接口)维护一个常量池,池中的数据项通过索引访问。
​ 由于受到方法区的限制,在创建类或接口时,如果运行时常量池的构造需要的内存超过Java虚拟机方法区域的可用内存,则Java虚拟机抛出OutOfMemoryError错误。

Direct Memory

​ 直接内存虽然不是jvm规范规定的一部分,但是这部分在虚拟机中被频繁的使用。
​ 本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制。配置虚拟机参数时,不要忽略直接内存,使得各个内存区域总和大于物理内存,从而导致动态扩展OutOfMemoryError异常。

为什么要直接内存

​ Java本身无法对磁盘进行读写,想要进行磁盘读写必须调用操作系统的方法(native),调用操作系统方法的时候,CPU会被切换到内核态。
​ 在内核态期间:CPU函数读取磁盘文件,同时在操作系统内存中开辟一块系统缓存区,会将读取的文件分次写入到系统缓存区,但是java不能直接读取系统缓存,所以java会在堆内存中开辟自己的缓存区(new byte[ ]),java再次从系统缓存区将数据读取到java缓冲区。循环完成读取操作。由于产生了两块缓存区域导致访问速度缓慢。
​ 内核态期间:调用ByteBuffer.allocateDirect()时,会分配一块直接内存区间(direct memory),java和系统可以共享该区域,减少一次读写操作。

参考
  • jvm虚拟机规范
  • 深入理解jvm虚拟机(周志明)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值