深入理解Java虚拟机--笔记1

Java内存区域与内存溢出异常


运行时数据区域


Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。

1 程序计数器--Program Counter Register

一块较小的内存空间,作用可视为当前线程所执行的字节码的行号指示器,没个线程有自己独立的程序计数器,即“线程私有”的内存区域。

若线程当前执行的是一个Java方法,则这个计数器记录的是当前执行的虚拟机字节码指令的地址;
若线程当前执行的是一个Native方法,则这个计数器值为空(Undefined)。

此内存区域为唯一一个在虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2 Java虚拟机栈--Java Virtual Machine Stack

“线程私有”的内存区域,生命周期和线程相同。

虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行时都会创建一个栈帧(Stack
Frame,方法运行期的基础数据结构)用于存放局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。

局部变量表存放着编译器可知的各种基础数据类型(boolean、byte、char、short、int、long、float、double)、对象引用(reference类型,它不等同于对象本身,依赖不同虚拟机实现,可能是一个执行对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
其中64为长度的long和double类型的数据会占用2个局部变量空间(Slot),其他的数据类型只占用1个。局部变量表的内存空间在编译期间完成分配。在方法运行期间不会改变局部变量表的大小。

Notes: Java虚拟机栈可能发生如下异常情况:

  • 如果线程请求分配的栈容量超过 Java 虚拟机栈允许的最大容量时,Java 虚拟机将会抛出一个 StackOverflowError
    异常。

  • 如果 Java
    虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是目前无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的虚拟机栈,那
    Java 虚拟机将会抛出一个OutOfMemoryError 异常。

3 本地方法栈--Native Method Stack(或C Stacks)

与Java Virtual Machine Stack很类似,区别在于Native Method
Stack是为虚拟机使用到得Native方法服务,而Java Virtual Machine Stack则是为虚拟机执行Java方法(即字节码)服务。

Note:

  • Native Method Stack抛出异常情况和虚拟机栈相同。

4 Java堆--Java Heap

Java Heap是被所有线程共享的一块内存区域,在虚拟机启动时创建。

所有的对象实例和数组都在Java堆上分配。

Java堆是GC管理的主要区域,因此经常被称为“GC 堆”(即Grabage Collected Heap)。

从内存回收的角度看,由于现在GC都是采用分代收集算法,故Java堆可分为:新生代、老年代。再细致一点有Eden空间、From Survivor空间、和To
Survivor空间等。 从内存分配的角度看,Java堆可划分出多个线程私有的分配缓冲区(Thread Local Allocation
Buffer,TLAB)。

根据虚拟机规范,Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可。

Note:

  • 如果堆中没有内存完成实例分配,并且堆也无法再扩展,则抛出OutOfMemoryError异常。

5 方法区--Method Area

Method Area和Java
Heap一样,也是线程共享的内存区域。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

GC回收行为在此区域比较少出现,主要回收目标是针对常量池和类型的卸载。

Note:

  • 根据Java虚拟机规范,当方法区无法满足分配需求时,则抛出OutOfMemoryError异常。

6 运行时常量池--Runtime Constant Pool

该区域为方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息以外,还有一项信息为常量池(Constant Pool
Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区中的运行时常量池中。

Note:

  • 当创建类或接口的时候, 如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最 大值,那 Java 虚拟机将会抛出一个
    OutOfMemoryError 异常。

一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储到运行时常量池中。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内容才能进入运行时常量池,运行期间也可能将新的常量放入池中(参见String类的intern()方法)。

Note:

  • 运行时常量池归属方法区,自然受到方法区内存的限制,当常量池无法再申请到内存是会抛出一个 OutOfMemoryError 异常。

7 直接内存--Direct Memory

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

时间: 2024-08-05 11:18:51

深入理解Java虚拟机--笔记1的相关文章

深入理解java虚拟机笔记(一)-java内存区域与内存溢出

1. 前言 这是深入理解java虚拟机一书的笔记,来自第二章.因为这本书讲的比较深奥,这是第二次看,需要记录一下笔记. 2. 运行时数据区域 java虚拟机所管理的内存分为以下几个区域. ps:图片来自网络 2.1 程序计数器 程序计数器是一块较小的内存空间,他可以看做是当前线程所执行字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选去下一条要执行的字节码指令,分之.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 这块内存是线程私有的内存. 如果线程在执行

深入理解Java虚拟机笔记---属性表集合

在Class文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息.与Class文件中其它的数据项目要求的顺序.长度和内容不同,属性表集合的限制稍微宽松一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性.为了能正确地解析Class文件,<Java虚拟机规范(第二版)>中预定义了9荐虚拟机实现应当能识别的属性,具体如下表所示: 对于每个属性,它的名称需要

深入理解Java虚拟机笔记---方法调用

方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.在程序运行时,进行方法调用是最普遍.最频繁的操作.在Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(相当于直接引用).这个特性给Java带来了更强大的动态扩展能力,但也使得Java方法的调用过程变得相对复杂,需要在类加载期间甚至到运行期间才能确定目标方法的直接引用.

深入理解Java虚拟机笔记---运行时栈帧结构

栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息.第一个方法从调用开始到执行完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程. 每一个栈帧都包括了局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息.在编译代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到了方法表的

深入理解Java虚拟机笔记---内存区域

Java虚拟机在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域有自各的用途,以及创建及销毁时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范(第2版)>规定,Java虚拟机管理的内存区域包括以下几个运行时数据区域,下如图 1.程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过

深入理解Java虚拟机笔记---垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对象垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的收集器可能会有很的差别,并且一般会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器.下面是Sun HotSpot虚拟机1.6版本Update22包含的所有收集器: 上图中,如果两个收集器之间存在连线,就说明它们可以搭配使用. 1.Serial收集器 Serial收集器是最基本.历史最悠久的收集器,曾经(在J

深入理解Java虚拟机笔记---class类文件结构概述

class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格紧凑地排列在class文件中,中间没有任何分隔符.当遇到需要占用8位字节以上的的数据项时,则会按照高位在前的方式侵害成若干个8位字节进行存储. 根据Java虚拟机规范的规定,class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构只有两种数据类型:无符号数和表.无符号数属于基于数据类型,以u1.u2.u4.u8来分别代码1个字节.2个字节.4个字节.8个字节的无符号数,无符号数可以用于描述数字.索引引用.数量值,或

深入理解Java虚拟机笔记——虚拟机类加载机制

[TOC] 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 动态加载和动态连接 在Java语言里面,类型的加载.连接和初始化过程都是在程序运行期间完成的. 类加载的时机 类的生命周期 类从被加载到虚拟机内存中开始,到卸载出内存位置,它的整个生命周期包括: 加载(Loading).验证(Verification).准备(Preparing).解析(Resolution).初始化(Ini

深入理解Java虚拟机笔记---字段表集合

字段表(field_info)用于描述接口或类中声明的变量.字段(field)包括了类级变量或实例变量,但不包括方法内部声明的变量.描述一个字段的信息有:字段的作用域(public,private,protected修饰符),是类级变量还是实例级变量(static修饰符),可变性(final),并发可见性(volatile修饰符,是否强制从主内存读写),是否可序列化(transient修饰符),字段数据类型(基本数据类型,对象,数组),字段名称.这些信息中,各个修改符都是布尔值,要么有某个修饰符