【学习笔记】Java虚拟机(二)内存区域与内存溢出
运行时数据区域
根据《Java虚拟机贵发(Java SE 7版本)》的规定,Java虚拟机所管理的内存包含以下几个运行时
数据区域。借图:
1、程序计数器
程序计数器(Program Counter Register)是一块较小的内存,可以看作是当前线程所执行的字节码
的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指
令,分之、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器。为了多线程每次切
换后能够恢复到正确的执行位置,每条线程都需要有一个独立存储的程序计数器,这种存储使用的内
存叫做“线程私有”内存。当线程执行Java方法时,计数器记录的就是正在执行的虚拟机字节码指令地
址,如果执行的是Native方法,计数器值为空(Undefined)。
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2、Java虚拟机栈
Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有内存,生命周期与线程相同。每个方法
执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等
信息,每个方法从调用到执行完毕的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。其中
局部变量表中存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、
long、double)、对象引用(reference类型,它可能是一个指向对象起始地址的引用指针或者是指
向一个代表对象的句柄)和returnAddress类型(指向一条字节码指令的地址)。其中64位长度的
long和double类型数据会占用2个局部变量空间(Slot),其余的占用一个。局部变量所需的内存在
编译期间完成分配,进入方法时所需的局部变量空间是完全确定的,运行期间不会改变。
Java虚拟机规范中对这个区域规定了两种异常状况:
① 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
② 如果虚拟机栈可动态扩展,在拓展时如果无法申请到足够的内存,抛出OutOfMemoryError
异常
3、本地方法栈
本地方法栈(Native Method Stack)与Java虚拟机栈的区别是虚拟机栈为虚拟机执行Java方法服务
,本地方法栈为虚拟机执行Native方法服务。所以有的虚拟机(如Sun HotSpot虚拟机)将本地方法
栈和虚拟机栈合二为一。
本地方法栈与虚拟机栈所抛出的异常一致。
4、Java堆
一般来说Java堆(Java Heap)是Java虚拟机管理的内存中最大的一块,它是被所有线程共享的区域
,在虚拟机启动时创建,唯一作用就是存放对象的实例以及数组。因为它是垃圾收集器主要管理的区
域,所以也叫GC堆(Garbage Collected Heap)。可以细分为新生代和老年代;再细致一点的有
Eden、From Survivor、To Survivor空间等,还可以划分出多个线程私有的分配缓冲区(Thread
Local Allocation Buffer,TLAB)。Java堆可以处于物理上不连续的内存空间中,可以固定大小,
也可以是可拓展的(通过-Xmx和-Xms控制)。
如果在堆中没有内存可以使用,并且也无法拓展,抛出OutOfMemoryError异常。
5、方法区
方法区(Method Area)别名Non-Heap(非堆)也是共享内存区域,用于存储已被虚拟机加载的类
信息、常量、静态变量、即时编译器编译后的代码等数据。HotSpot早期使用“永久代”(Permanent
Generation)来实现方法区,好处是可以用垃圾收集器来一起管理方法区,但坏处是容易遇到内存溢
出的问题(永久代有-XX:MaxPermSize的内存上限设置),而且有极少的方法(如String.intern
())会因此导致不同虚拟机下有不同的结果,所以HotSpot也打算逐步使用Native Memory来实现
方法区,在JDK1.7中已经将永久代中的字符串常量池移出。方法区和Java堆一样可以处于物理上不连
续的内存空间中,可以固定大小也可以是可拓展的,并且可以不实现垃圾回收。但实际上垃圾回收还
是有必要的,针对方法区的回收主要是常量池回收和类的卸载。
如果在方法区中没有内存可以使用,并且也无法拓展,抛出OutOfMemoryError异常。
6、运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分,Class文件中除了有类的版本、字段
、方法、接口等描述信息外,还有一项信息就是常量池(Runtime Pool Table),用于存放编译期生
成的各种字面量和符号引用,这部分信息将在类加载后进入方法区的运行时常量池。一般来说除了保
存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在这个区域。Java语言不要求
常量只在预置入Class文件中常量池的时候才能进入运行时常量池,运行期间也可以动态放入(如
String.intern())。
运行时常量池属于方法区,抛出异常的规则也与方法区一致。
7、直接内存
直接内存(Direct Memory)不是虚拟机运行时数据区的一部分,也不是Java规范中定义的内存区域
。在JDK1.4中新加入了NIO(New Input/Output)类, 引入了基于通道(Channel)与缓冲区
(Buffer)的I/O方式,可以使用Native函数直接分配堆外内存,然后通过存储在Java堆中的
DirectByteBuffer对象作为这块内存的引用进行操作,避免了Java堆和Native堆来回复制数据的操作
。本机直接内存不受Java堆大小的限制,但会受到本机总内存(包括RAM和SWAP区或者分页大小)
大小以及处理器寻址空间的限制。
各个区域总和大于物理内存限制(包括物理的和操作系统级的限制)从而导致动态扩展失败时抛出
OutOfMemoryError异常。
【学习笔记】Java虚拟机(二)内存区域与内存溢出相关推荐
- 深入理解Java虚拟机(第3版)学习笔记——JAVA内存区域(超详细)
深入理解Java虚拟机(第3版)学习笔记--JAVA内存区域(超详细) 运行时数据区域 程序计数器 java虚拟机栈 本地方法栈 java堆 方法区 运行时常量池 直接内存 对象的创建 对象的内存布局 ...
- 【深入理解Java虚拟机学习笔记】第二章 Java 内存区域与内存溢出异常
最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...
- 深入理解Java虚拟机(二)Java内存区域与内存溢出异常
一.前言 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好 ...
- 《深入理解JAVA虚拟机》详细解读(第二章 ):JAVA内存区域与内存溢出异常
目录 一.JAVA内存区域与内存溢出异常 1. 概述 2. 运行时数据区域 2.1 程序计数器 2.2 Java虚拟机栈 2.3本地方法栈 2.4 堆 2.5 方法区 2.6 运行时常量池 2.7直接 ...
- 0x00000000指令引用的内存不能为written_「深入Java虚拟机」Java内存区域与内存溢出...
内存区域 Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java虚拟机规范将JVM所管理的内存分为以下几个运行时数据区:程序计数器.Java虚拟机栈.本地方法栈 ...
- 《深入理解JAVA虚拟机》周志明 第三版 - 第二章 JAVA内存区域与内存溢出异常
一. 概述 在虚拟机自动内存管理机制下,不容易出现内存泄漏和内存溢出问题,但是一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误.修正问题将会成为一项异常艰难的工作. 二. ...
- 深入理解Java虚拟机——第二章——Java内存区域与内存溢出异常
运行时数据区域 Java虚拟机运行时数据区域 程序计数器 程序计数器可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选取下一条所需要执行的字节码指令,分支. ...
- java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- Java虚拟机——Java内存区域与内存溢出
内存区域 Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java虚拟机规范将JVM所管理的内存分为以下几个运行时数据区:程序计数器.Java虚拟机栈.本地方法栈 ...
- 【深入Java虚拟机】之一:Java内存区域与内存溢出
内存区域 Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java虚拟机规范将JVM所管理的内存分为以下几个运行时数据区:程序计数器.Java虚拟机栈.本地方法栈 ...
最新文章
- ntoskrnl损坏
- java irowset行数,Java CloneableRecord.put方法代码示例
- NET问答: 为什么仅有 getter 的属性,还可以在构造函数中赋值 ?
- LeetCode每日打卡 - 汉明距离总和
- python函数示例_带Python示例的complex()函数
- Python机器学习:梯度下降法005梯度下降法和数据标准化
- [转]Resource for Windows Phone 7
- ubuntu 13.10 64位安装及配置 google拼音输入法及Adobe flash player、mp3 插件安装、中文GBK编码等 -转
- executorservice 重启_iPhone7使用久了突然手机自动重启,多半原因出在这儿,进来看看...
- springboot之websocket集成
- 【SimpleITK】坐标次序问题
- 网络模型 OSI七层协议和TCP/IP四层协议
- saas和pass的区别
- JavaScript 基础知识总结归纳(一)
- 教你如何赚你的第一桶金 - 2048(包括源代码)
- 浅谈LCD液晶屏和LED屏,它们有什么不同之处
- 人人转联盟官网?人人转联盟是真的吗?人人转联盟能赚钱吗?
- 克己慎独 2008-9-23 13:32:00 (21ic)
- 野火电机+CUBEMX——直流有刷电机的实现
- 解决Angular Kendo UI 导出PDF中文乱码
热门文章
- 我为什么喜欢周六来加班?
- Dva.js 快速上手指南
- 虚拟机硬盘资源调整(分区)
- 题目:请编写函数void fun( char *s),函数的功能是:在字符穿中的所有数字字符前加一个$字符,例如,输入:A1B23CD45,则输出为:A$1B$2$3CD$4$5。
- 01 -- 机器学习入门
- 第三方支付接口示例代码
- Cowardly refusing to save to a terminal. Use the -o flag or redirect
- shell bash脚本_如何在Windows 10上创建和运行Bash Shell脚本
- SoruceTree 中移除和丢弃
- 软件工程网络15个人阅读作业1 201521123056 吴剑通