Java的动态内存分配和垃圾回收机制使java程序员不用像C++程序员那么头疼内存的分配与回收。相信熟悉COM机制的朋友对于引用计数管理内存的方式深有感触。
Java虚拟机的自动内存管理不仅降低了编码的难度而且不容易出现内存泄露和内存溢出的问题。但是这过于美好的愿景正是由于把内存的控制权交给了Java虚拟机,一旦出现内存泄露和溢出,我们就必须翻过Java虚拟机自动内存管理这堵高墙去排查错误。本文简要总结下JVM运行时数据区域的划分、作用以及可能出现的异常。
图1 Java虚拟机运行时数据区
如图1所示,根据《Java虚拟机规范(Java SE 7 Edition)》的规定,Java虚拟机在执行Java程序时,即运行时环境下会把其所管理的内存划分为几个不同的数据区域。有的区域伴随虚拟机进程的启动而创建,死亡而销毁;有些区域则是依赖用户线程的启动时创建,结束时销毁。所有线程共享方法区和堆,虚拟机栈、本地方法栈和程序计数器是线程隔离的数据区。
1.1程序计数器Program Counter Register
由于操作系统通过时间片轮流的多线程并发方式,任何时刻处理器只会处理当前线程的指令。线程间切换的并发要求每个线程都需要有一个私有的程序计数器,程序计数器间互不影响。
程序计数器存储当前线程下一条要执行的字节码的地址,占用内存空间较小。所有的控制执行流程,分支、循环、返回、异常等功能都在程序计数器的指示范围之内,字节码解释器通过改变程序计数器的值来获取下一条要执行的字节码的指令。
1.2 虚拟机栈 VM Stack
简单点说,虚拟机栈就是类中的方法的执行过程的内存模型。虚拟机栈也是线程私有的,并且同线程的生命周期相同。
对于方法的调用,有必要先介绍下栈帧(Stack Frame)。
虚拟机在执行每个方法的调用时会创建一个栈帧的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。每个方法的调用过程,就对应着一个栈帧在虚拟机里的入栈出栈的过程。栈帧包括了方法的局部变量表、操作数栈、动态链接和方法出口等一些额外的附加信息。对于活动线程中栈顶的帧栈,称为当前栈帧,这个栈帧所关联的方法称为当前方法,正在执行的字节码指令都只针对当前有效栈帧进行操作。
在栈帧的基础上,不难理解虚拟机栈的内存结构。Java虚拟机规范规定虚拟机栈的大小是可以固定的或者动态分配大小。Java虚拟机实现可以向程序员提供对Java栈的初始大小的控制,以及在动态扩展或者收缩Java栈的情况下,控制Java栈的最大值和最小值。
以下异常情况与Java栈相关:
- 如果线程请求的栈深度大于虚拟机所允许的深度,则Java虚拟机将抛出StackOverflowError异常。
- 如果虚拟机栈可以动态扩展,但是无法申请到足够的内存来实现扩展,或者不能得到足够的内存为一个新线程创建初始Java栈,则Java虚拟机将抛出OutOfMemoryError异常。
1.3本地方法栈 Native Method Stack
本地方法栈内执行的是非Java语言编写的代码,比如C或C++,而虚拟机栈执行的是java方法字节码服务,这是两者最大的区别。本地方法栈的是虚拟机使用本地方法服务的,如果提供本地方法栈,则它们通常在每个线程被创建时分配在每个线程基础上的。
同虚拟机栈一样,本地方法栈也会出现与虚拟机栈类似的异常,也会抛出StackOverflowError和OutOfMemoryError异常。
1.4 Java堆 Java Heap
Java堆是类实例和数组的分配空间,是一块所有线程共享的内存区域。堆在虚拟机启动时创建,是Java虚拟机所管理的内存中最大一块。内存泄露和溢出的问题大都出现在堆区域,由此,Java堆是垃圾回收的主要重点管理区。
从内存回收的角度看,由于现在收集器基本上都是采用的分代收集算法,Java堆还可细分为新生代和老年代;从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓存区。这种进一步的内存划分方式目的是更好地回收内存,或者更快地分配内存。
Java虚拟机规范规定堆在内存单元中只要在逻辑上是连续的即可,Java堆可以是固定大小的,或者按照需求做动态扩展,并且可以在一个大的堆变的不必要时收缩。Java虚拟机的实现向程序员或者用户提供了对堆初始化大小的控制,以及对堆动态扩展和收缩的最大值和最小值的控制。
以下异常情况与Java堆相关:
- 如果堆中没有可用内存完成类实例或者数组的分配,在对象数量达到最大堆的容量限制后将抛出OutOfMemoryError异常。
1.5 方法区 Mehod Area
方法区在虚拟机启动时创建,也是一块所有线程共享的内存区域。方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。用一句话说就是方法区类似于传统语言的编译后代码的存储区。
虽然Java虚拟机规范在逻辑上把方法区描述为堆的一个部分,但是在垃圾回收方面的限制却比较宽松,宽松到方法区可以不用实现垃圾回收。但是,垃圾回收在方法区还是必须有的,只是回收效果不是很明显。这个区域的回收目标主要针对的是常量池的回收和对类型的卸载。
方法去的大小也可控制,以下异常与方法区相关:
- 如果方法区无法满足内存分配需求时,将会抛出OutOfMemoryError异常。
1.6运行时常量池 Runtime Constant Pool
常量池是每个类的Class文件中存储编译期生成的各种字面量和符号引用的运行期表示,其数据结构是一种由无符号数和表组长的类似于C语言结构体的伪结构,详细内容请参考《Java虚拟机规范第七版》第四章。
常量池也是方法区的一部分,类的常量池在该类的Java class文件被java虚拟机成功地装载时创建,这部分内容在类加载后存放到方法区的运行时常量池中。
运行时常量池属于方法区,自然也受到方法区内存大小的限制,以下异常与常量池有关:
- 在装载class文件时,如果常量池的创建需要比Java虚拟机的方法区中需求更多的内存时,将会抛出OutOfMemoryError异常。
通过总结,对于虚拟机运行时数据区域的划分及每个区域作用,存储内容及可能出现的异常有了一个大致的了解。Java的自动内存分配和垃圾回收筑起的这道高墙,在出现内存泄漏或者溢出的情况下,这道高墙就必须翻越了。
相关推荐
### JVM 运行时数据区域、垃圾回收机制与类加载机制详解 #### 一、运行时数据区域 Java虚拟机(JVM)的核心组件之一便是其运行时数据区域,这一区域负责存储程序运行过程中产生的各种数据。为了更好地理解这部分内容...
#### 二、JVM运行时数据区详细介绍 ##### 1. 程序计数器(The Program Counter Register) - **功能介绍**:程序计数器(PC寄存器)用来指示当前线程所执行的字节码指令的地址。每个线程都有一个独立的程序计数器...
本文将围绕JVM运行时数据区域进行详细介绍,包括程序计数器、Java虚拟机栈、本地方法栈、方法区以及Java堆等组成部分。 #### 二、程序计数器 **定义**:程序计数器是一块较小的内存空间,它可以被视为当前线程所...
Java内存模型与JVM运行时数据区的区别详解 Java内存模型是Java语言在多线程并发情况下对于共享变量读写的规范,主要是为了解决多线程可见性、原子性的问题,解决共享变量的多线程操作冲突问题。Java内存模型是为了...
JVM运行时数据区是Java虚拟机中最重要的组成部分之一,它是JVM运行时的核心区域,负责存储和管理程序运行时需要的数据。根据JVM规范,JVM运行时数据区可以分为五个部分:PC寄存器、虚拟机栈、方法区、运行时常量池和...
JVM的运行时数据区是指JVM在运行时使用的一些区域,用于存储被JVM自身或者在JVM上运行的程序所使用的数据。这些区域包括: 1. 方法区(Method Area):用于存储类信息、字段信息和方法信息的区域。 2. 堆(Heap):...
03 JVM 运行时数据区概述及线程的 PPT 重绘。讲述 Java 虚拟机 运行时数据区所处位置,结构划分,以及各个区域与线程的关系。
首先,我们来看JVM运行时数据区的组成部分: 1. **方法区(Method Area)**:这是一个全局共享的内存区域,存储了类信息、常量、静态变量以及编译后的代码。这部分内存被称为Non-Heap,当其内存不足时,会抛出`...
在深入学习Java编程语言的过程中,理解Java虚拟机(JVM)的运行时数据区域是至关重要的。JVM的运行时数据区是程序运行时存储数据的地方,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆以及方法区。以下是这些...
HotSpot VM是Oracle JDK中默认的JVM实现,它提供了一种称为HSDB(HotSpot Debugger Bridge)的工具,用于在运行时查看和分析JVM内部的数据。本篇将通过HSDB的使用案例来探讨HotSpot VM的运行时数据。 首先,我们...
- JVM允许在运行时动态加载类和资源,适应不同环境的需求。 总结来说,JVM的运行机制是Java平台无关性的关键,它通过字节码和解释器实现了代码的跨平台执行,同时通过垃圾回收和内存管理提供了高效且安全的运行...
此外,直接内存不属于JVM运行时数据区的一部分,但被频繁使用,如在Java NIO中用于基于Channel与Buffer的IO操作。 3. JVM执行引擎与垃圾回收: JVM的执行引擎负责解释字节码指令,它通常包括即时编译器和解释器两种...
在JVM内部,程序的运行依赖于一系列的运行时数据区,这些区域各自负责存储不同的数据和执行状态。本文将深入探讨JVM的运行时数据区,以帮助读者理解Java程序在运行过程中的内存管理机制。 首先,我们要明确的是,...
本文对 JVM 内存分配与垃圾回收进行了详细的解释,包括 JVM 运行时数据区域、String 常量池、垃圾回收、垃圾收集算法等内容。了解 JVM 内存分配与垃圾回收是 Java 开发者必须掌握的基础知识之一。
【标题】:“深入理解Java内存模型:从JVM运行时数据区域分析t1、t2、t3的位置” 【描述】:本篇文章旨在探讨Java内存模型中不同类型的变量——包括静态变量t1、实例变量t2和局部变量t3——在HotSpot VM中的存储...
### JVM运行时数据区 #### 程序计数器 程序计数器是当前线程所执行的字节码的行号指示器,是线程私有的内存区域。由于Java是多线程并发执行的,每个线程都需要有一个独立的程序计数器,以便记录线程切换后继续执行...
运行时数据区是 JVM 运行时的内存模型,它包括方法栈、PC 寄存器、本地方法栈、Java 堆、方法区和直接内存等部分。执行引擎负责读取运行时数据区的字节码,并逐个执行它们。 类加载器子系统是 JVM 的一个重要组成...
常量池静态常量池即*.class文件中的常量池,用于存放字面量和符号引用运行时常量池是jvm运行期间,存储常量的数据结构运行时常量池概念运行时常量池(Runti
JVM 的内存结构可以分为 6 个区域:PC 寄存器、JVM 栈、堆(Heap)、方法区、运行时常量池和直接内存。每个区域都有其特定的功能和作用,以下是对每个区域的详细介绍: PC 寄存器(PC Register) PC 寄存器是一块...