zoukankan      html  css  js  c++  java
  • 运行时数据区

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

    程序计数器

    程序计数器(Program Counter Register)是一块较小的内存空间,可以看做是字节码的行号指示器,字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

    程序的分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。

    在多线程环境下,为了线程切换后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

    如果线程执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果是本地(Native)方法,这个计数器值为空。

    虚拟机栈

    java虚拟机栈(Java Virtual Machine Stack)也是线程私有的。

    虚拟机栈描述的是java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

    我们可以知道,虚拟机栈中的数据都是以栈帧的形式存在的。那么我们可以对栈帧的结构进行解析

    局部变量表

    局部变量表存放了编译期可知的各种数据:

    • 基本数据类型(boolean、byte、char、short、int、float、long、double)

    • 对象引用(Reference类型)

    • returnAddress类型。

    局部变量表的数据类型是以局部变量槽(Slot)来表示的。

    long和double类型的数据占用两个变量槽,其余数据类型只占一个。

    由于数据是在编译期已知的数据,所以在方法运行期间不会改变局部变量表的大小(变量槽的数量确定),实际占用的大小由具体的虚拟机决定。

    在虚拟机栈中规定了两种异常状况:

    • 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
    • 如果虚拟机栈容量可以动态扩展,当栈扩展无法申请到足够的内存会抛出OutOfMemoryError异常。

    本地方法栈

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

    在这个内存产生的异常与虚拟机栈相同。

    有的虚拟机,比如HotSpot就将本地方法栈和虚拟机栈合二为一。

    Java堆

    《Java虚拟机规范》中对Java堆的描述是:“所有对象实例以及数组都应当在对上分配”。虽然随着即时编译技术的进步,这句话可能并不是非常绝对,但绝大部分对象都是从堆上分配的。

    Java堆是垃圾收集管理的内存区域。

    Java堆是所有线程共享的内存区域,但是可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB),以提升对象分配的效率。

    Java堆可以处于物理上不连续的内存空间,但在逻辑上应该被视为连续。

    Java堆可以是固定大小的,也可以是可扩展的。可以通过参数-Xms和-Xmx设定。

    在java堆没有内存完成实例分配,并且堆也无法扩展时,抛出OutOfMemoryError异常。

    方法区

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

    方法区以前也被称为永久代,因为HotSpot虚拟机使用永久代来管理方法区,但是永久代来实现方法区会导致java应用更容易遇到内存溢出的状况。

    因此在JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

    JDK1.8开始,取消了Java方法区,取而代之的是位于直接内存的元空间(metaSpace)。

    运行时常量池

    运行时常量池(Runtime Constant Pool)是方法区的一部分,存放类加载的各种字面量与符号引用。

    运行时常量池具备动态性,运行期间也可以将新的常量放入池中。

    直接内存

    直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分。但是这部分内存也被经常使用,也会导致OutOfMemoryError异常。

    通过NIO可以用java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,可以显著提高新能,因为避免了在java堆和Native堆中来回复制数据。

    直接内存不会受java堆限制,但是受到本机总内存大小以及处理器寻址空间的限制。一般服务器管理员配置虚拟机参数时,会根据实际内存去设置-Xmx等参数信息,但是经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoruErro异常。

  • 相关阅读:
    ecshop 调用指定分类的推荐,热卖,新品
    ecshop 首页调用指定类产品
    html常用笔记
    ecshop 修改flash图片大小
    ecshop 删除随机版权
    Java Web(一) Servlet详解!!
    Git使用总结
    git clone命令使用
    Lucene学习总结之四:Lucene索引过程分析
    Lucene学习总结之二:Lucene的总体架构
  • 原文地址:https://www.cnblogs.com/ylcc-zyq/p/12863473.html
Copyright © 2011-2022 走看看