精华内容
下载资源
问答
  • JVM运行时数据区

    2020-10-24 22:27:11
    JVM运行时数据区

    程序计数器

    程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

    Java虚拟机栈

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

    本地方法栈

    本地方法栈(Native Method Stacks) 与虚拟机栈所发挥的作用是非常相似的, 其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码) 服务, 而本地方法栈则是为虚拟机使用到的本地(Native)方法服务,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

    Java堆是被所有线程共享的一块内存区域, 在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例.
    如果在Java堆中没有内存完成实例分配, 并且堆也无法再扩展时, Java虚拟机将会抛出OutOfMemoryError异常。

    方法区

    方法区(Method Area) 与Java堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类型信息、 常量、 静态变量、 即时编译器编译后的代码缓存等数据。
    这区域的内存回收目标主要是针对常量池的回收和对类型的卸载, 元空间 如果方法区无法满足新的内存分配需求时, 将抛出OutOfMemoryError异常。

    展开全文
  • JVM 运行时数据区

    2021-04-04 18:51:54
    JVM 运行时数据区 1 .运行时数据区组成概述 JVM运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从 Java 虚拟机规范,Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域: ...

    JVM 运行时数据区

    1 .运行时数据区组成概述

    JVM 的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从 Java 虚拟机规范,Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域:

    2.程序计数器

    1.概述

    JVM 中的程序计数寄存器(Program Counter Register)中的 Register 命名源于CPU 的寄存器,寄存器存储指令相关的现场信息.CPU 只有把数据装载到寄存器才能运行. 这里,并非是广义上所指的物理寄存器,或许将其翻译为 PC 计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会.JVM 中的PC 寄存器是对物理 PC 寄存器的一种抽象模拟.

    2.作用

    程序计数器用来存储下一条指令的地址,也即将要执行的指令代码.由执行引擎读取下一条指令.

    • 它是一块很小的内存空间,几乎可以忽略不计,也是运行速度最快的存储区域.

    • 在 JVM 规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程生命周期保持一致.

    • 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法.程序计数器会存储当前线程正在执行的Java方法的JVM指令地址.如果是在执行native方法,则是未指定值(undefined).

    • 它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成.

    • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令.

    • 它是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域.

    3. Java 虚拟机栈

    1.背景

    由于跨平台性的设计,Java 的指令都是根据栈来设计的.不同平台CPU 架构不同,所以不能设计为基于寄存器的.基于栈的指令设计优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样功能需要更过的指令集.

    2.栈和堆的区别

    栈是运行时的单位,堆是存储时的单位

    栈解决的是程序的运行问题 即程序如何处理数据

    堆解决的是程序的存储问题 即数据放在哪,怎么放

    3. Java 虚拟机栈是什么?

    • Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈.

    • 每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次方法的调用.

    • Java 虚拟机栈是线程私有的.

    • 生命周期和线程一致.

    4.作用

    主管 Java 程序的运行,它保存方法的局部变量(8 种基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回.

    5.特点

    • 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器.

    • JVM 直接对 java 栈的操作只有两个:调用方法进栈. 执行结束后出栈.

    • 不存在垃圾回收问题

    6.栈中存储什么

    • 每个线程都有自己的栈,栈中的数据都以栈帧为单位存储.

    • 在这个线程上正在执行的每个方法都各自对应一个栈帧.

    • 栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息.

    7.栈的运行原理

    1. JVM 直接对 java 栈的操作只有两个,就是对栈帧的进栈和出栈,遵循”先进后
      出”/后进先出的原则.
    2. 只有当前在执行的方法的栈帧(栈顶)是有效地,这个栈帧被称为当前栈,与当前栈帧对应的方法称为当前方法,定义这个方法的类称为当前类.
    3. 执行引擎中的指令只针对当前栈进行操作
    4. 如果该方法调用了其他方法,那么对应的新的栈帧就会被创建,成为当前栈帧.

    **注:**不能在栈中引用其他栈帧(方法)

    Java 方法有两种返回的方式,一种是正常的函数返回,使用 return 指令,另一种是抛出异常.不管哪种方式,都会导致栈帧被弹出.

    8.栈帧的内部结构

    每个栈帧中存储着:

    1. 局部变量表(Local Variables)
      局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。
    2. 操作数栈(Operand Stack)(或表达式栈)
      栈最典型的一个应用就是用来对表达式求值。在一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
    3. 动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)
      因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
    4. 方法返回地址(Retuen Address)(或方法正常退出或者异常退出的定义)
      当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

    9.本地方法栈

    1. 虚拟机管理 java 方法的调用,而本地方法栈用于管理本地方法的调用
    2. 本地方法栈是私有的
    3. 本地方法栈是用C语言写的
    4. 它的具体做法是在 Native Method Stack 中登记 native 方法,在Execution Engine 执行时加载本地方法库.
    5. 允许被实现成固定或者是可动态扩展的内存大小.内存溢出方面也是相同的
    展开全文
  • JVM 运行时数据区

    2015-07-15 19:10:19
    翻译人员: 铁锚 翻译时间: 2013年11月11日 原文链接:...图1 JVM运行时数据区 1. 单线程数据区(非共享) 从上图可以看出,每个线程都有自己独立的数据区,包括 PC(程序计数器),JVM(方法)栈,以及本地方法栈. 当创建新线...

    翻译人员: 铁锚
    翻译时间: 2013年11月11日
    原文链接: JVM Run-Time Data Areas
    参考地址: JVM运行时是什么样子?

    这是阅读JVM规范时的笔记, 我画了一幅图来帮助自己加深理解:

    图1 JVM运行时数据区

    1. 单线程数据区(非共享)
    从上图可以看出,每个线程都有自己独立的数据区,包括 PC(程序计数器),JVM(方法)栈,以及本地方法栈. 当创建新线程时,这些数据区域将会一并创建.

    程序计数器: 英文名字是 Program Counter Register,简称PC, 用于记录每个线程执行到哪个地方了(可以认为程序是指令的序列,PC记录着下一条(或本条)应该执行的指令的地址).
    JVM方法栈:  包含如下面的图所示的一些栈帧(frame).
    本地方法栈: 英文为 Native Method Stack,顾名思义,适用于供 native方法使用的内存空间,本地方法(native method)指不用Java语言开发的方法,如 C,C++,PB等编译型语言所开发的动态链接库(中的方法).

    2. 所有线程共享数据区
    堆内存 和 方法区 是所有线程共享的数据区.

    堆内存: 英文名称 Heap,是Java编程最频繁使用的内存区域. 其中存储着 数组和对象,当 JVM启动时即创建此内存区域, 垃圾回收主要是指堆内存的回收。
    方法区: Method Area,存储了 运行时常量池,类结构信息(field and method data) 以及 方法,构造器的代码.
    运行时常量池: Runtime Constant Pool, 每一个类或者接口,在其编译后生成的 .class文件中,有一个部分叫做 常量表(constant_pool_table),JVM将class文件加载以后,就解析常量表的内容到运行时常量池. 包括编译时方法中就明确可知的数字值,String值, 以及必须在运行时解析的属性域引用(field reference).

    图2 JVM栈

    JVM栈(Stack)由一个个的 栈帧(Frame)组成,当一个方法被调用时会自动压入一个栈帧到Stack中. 每个栈帧(frame)由局部变量区,操作数栈,以及对常量(池)的引用组成.

    想要了解更详细的信息,建议访问: JVM官方文档.

    相关文章:

    1. JVM运行时是什么样子?
    2. What does a Java array look like in memory?
    3. What can we learn from Java HelloWorld?
    4. A simple Swing GUI example for Observer Design Pattern
    展开全文
  • Jvm 运行时数据区

    2020-04-09 19:39:24
    本文描述的有关于 JVM运行时数据区是基于 HotSpot 虚拟机。 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着...

    写在前面

    本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机。

    概述

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

    HotSpot 运行时数据区

    运行时数据区在 HotSpot 1.8 之前的版本和 1.8 版本有所不同,主要是 方法区移到元空间 了。

    在这里插入图片描述

    图 1-1:JDK1.8 之前 JVM 运行时数据区

    在这里插入图片描述

    图 1-2:JDK1.8 JVM 运行时数据区

    线程私有区域

    程序计数器(PROGRAM COUNTER REGISTER)

    程序计数器是一块很小的区域,它存储的是当前线程正在执行的字节码的地址(在这里,其实有两个“当前”,一个是:当前正在被 CPU 执行的线程,另一个是:当前这个被执行的线程中正在被执行的字节码指令)。字节码解释器工作时就是改变程序计数器的值来选取下一条需要执行的字节码。对于单核心而言,多线程是通过线程轮流切换的方式实现的,在任一时刻只有一个线程能够得到 CPU 的执行权从而执行线程中的字节码指令,因此,为了使线程切换后能够恢复到正在执行的字节码的位置,每个线程都需要拥有自己的程序计数器。

    注意:程序计数器是唯一的一块在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 的区域。由于它是线程私有的,所以它的生命周期随着线程的创建而创建,随着线程的结束而死亡 。

    虚拟机栈(VM STACK)

    虚拟机栈也是线程私有的,所以它的生命周期与程序计数器相同。虚拟机栈描述的是 Java 方法执行的内存模型。

    每个方法在执行的时候都会创建一个栈帧(一个方法对应一个栈帧,栈帧即栈的基本单位)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被线程执行从开始到结束,就对应着一个栈帧在虚拟机栈中入栈(压栈)和出栈(弹栈)的过程。局部变量表中存放了编译可知的各种基本数据类型(byte,short,int,long,float,double,char,boolean)、对象引用(reference 类型,它存储的是:对象的地址或者是指向代表对象的句柄)。

    Java 虚拟机规范中规定了虚拟机栈可能出现的两种异常状况:StackOverflowError 和 OutOfMemoryError。

    StackOverflowError: 若当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候会抛出 StackOverflowError。

    OutOfMemoryError: 若虚拟机栈动态扩展过程中,如果线程请求申请栈空间无法申请到足够的内存,就会抛出 OutOfMemoryError。

    本地方法栈(NATIVE METHOD STACK)

    本地方法栈与虚拟机栈类似,虚拟机栈是执行 Java 方法开辟的内存空间,而本地方法栈是执行 Native 方法开辟的内存空间。

    与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError 异常,抛出条件也是类似的。

    线程间共享的内存区域

    堆(HEAP)

    堆是所有线程共享的一块区域,主要用来存放对象和数组。

    在 Java 虚拟机规范中有描述:所有的对象实例和数组都要在堆上分配,但是 随着 JIT(JUST-IN-TIME)编译器的发展与逃逸分析技术的逐渐成熟,并不是所有对象都只在堆上分配了,比如:随着逃逸分析技术的逐渐成熟,在即时能被回收的对象也有可能会在虚拟机栈上分配。

    由于现在都采用分代回收算法,所以从内存回收的角度来看,堆还可以细分为:新生代、老年代。新生代又可以分为:Eden 空间、From Survivor 空间、To Survivor 空间。

    注意:1.8 中已经彻底将方法区的实现由之前的永久代改为元空间。

    堆里面可能抛出的异常就是 OutOfMemoryError, 出现这种错误的表现形式主要有两种:

    OutOfMemoryError: GC Overhead Limit Exceeded:当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。

    java.lang.OutOfMemoryError: Java heap space:假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfMemoryError: Java heap space 错误。

    方法区(METHOD AREA)

    方法区和堆一样也是所有线程共享的一块区域,主要用来存储已经被虚拟机加载的类信息、常量(final 修饰的)、静态变量、即时编译器(JIT)编译后产生的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

    永久代就是方法区域?

    早些时候,很多开发者更愿意称方法区为“永久代”。其实“永久代”这个称呼的由来是因为 HotSpot 团队并不打算为方法区重新设计垃圾回收算法,为了在方法区中能够沿用堆中的分代回收算法,所以按照堆中的命名方式,将方法去称为“永久代”。对于 JRocket、J9 而言是不存在“永久代”的概念的,所以当 HotSpot 1.8 和 JRocket 合并时,就彻底放弃了“永久代”的概念(其实从 1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。

    方法区的垃圾回收很困难!!!

    由于 Java 虚拟机规范对方法区的限制非常松,甚至可以不实现垃圾回收,一般而言,这个区域的内存回收很不令人满意,尤其是类型的卸载,条件非常苛刻,但是由于现代框架大量的依赖于 JIT 技术,导致方法区的占用比逐渐提高,所以对于方法区的回收至关重要。根据 Java 虚拟机规范规定,当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。

    运行时常量池(RUNTIME CONSTANT POOL)

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

    这块区域在 1.7 之前原来是方法区的一部分,Class 文件中有一项信息是常量池(或者说是一张常量表,Class 文件以表存储数据)。
    在这里插入图片描述

    图 1-3:Class 文件常量池

    运行时常量池存储的东西较为复杂,主要分为字面量和符号引用

    字面量

    存放的字面量主要包括 常量(final 修饰的),比如:final int x = 1、静态变量(static 修饰的),还有一些其他的字面量。

    符号引用

    符号引用主要包括:类的完全限定名、字段名称和描述符、方法名称和描述符,包括很多符号,比如:() 也可以看做符号引用。

    字面量和符号引用将在类加载(ClassLoader 加载 Class 字节码文件)后进入方法区的运行时常量池中存放。不过,除了保存 Class 文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。运行时常量池相对于 Class 文件常量池一个重要的特征就是具备动态性,Java 语言并不要求常量一定产生于编译期的 Class 文件的常量池中,也并不是只有 Class 文件常量池中的常量才能够进入运行时常量池中,在线程执行方法的过程当中可能产生新的常量存放到运行时常量池中,例如:String 类的 intern() 方法。当运行时常量池无法申请到内存的时候就会抛出 OutOfMemoryError 异常。

    直接内存(DIRECT MEMORY)

    直接内存并不是 JVM 运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。

    在 JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel) 与缓存区(Buffer) 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。

    本机直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。

    总结

    Java 虚拟机包含的内容很多,本篇文章也只是对 Java 内存管理模块的 Java 虚拟机运行时数据区做了简要的分析,关于内存管理模块的其他部分后续会继续更新,敬请期待!

    参考

    • 《深入理解 Java 虚拟机·JVM 高级特性与最佳实践(第 2 版》
    • https://mp.weixin.qq.com/s/EZ4DDTC0CSqRQlqJLGSV-g
    • https://blog.csdn.net/qq_41212104/article/details/80723644
    • https://www.cnblogs.com/chanshuyi/p/jvm_serial_06_jvm_memory_model.html
    • https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F?id=%e5%86%99%e5%9c%a8%e5%89%8d%e9%9d%a2-%e5%b8%b8%e8%a7%81%e9%9d%a2%e8%af%95%e9%a2%98

    公众号

    如果大家想要实时关注我更新的文章以及我分享的干货的话,可以关注我的公众号 我们都是小白鼠

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,702
精华内容 3,880
关键字:

jvm运行时数据区