没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
与 之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,
墙外面的人想进去,墙里面的人却想出来。
概述:
对于从事 、程序开发的开发人员来说,在内存管理领域,他们即是
拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所
有权”,又担负着每一个对象生命开始到终结的维护责任。
对于 程序员来说,不需要在为每一个 操作去写配对的
,不容易出现内容泄漏和内存溢出错误,看起来由 管理内存一切都很
美好。不过,也正是因为 程序员把内存控制的权力交给了 ,一旦出现
泄漏和溢出,如果不了解 是怎样使用内存的,那排查错误将会是一件非常
困难的事情。
VM 运行时数据区域
执行 程序的过程中,会使用到各种数据区域,这些区域有各自的
用途、创建和销毁时间。根据《 虚拟机规范(第二版)》(下文称
)的规定, 包括下列几个运行时数据区域:
程序计数器():
每一个 线程都有一个程序计数器来用于保存程序执行到当前方法的哪
一个指令,对于非 方法,这个区域记录的是正在执行的 原语的
地址,如果正在执行的是 方法,这个区域则为空(!)。
此内存区域是唯一一个在 中没有规定任何 ""#$
情况的区域。
% 虚拟机栈(&')
与程序计数器一样, 栈的生命周期也是与线程相同。 栈描述的是
方法调用的内存模型:每个方法被执行的时候,都会同时创建一个帧
(()用于存储本地变量表、操作栈、动态链接、方法出入口等信息。
每一个方法的调用至完成,就意味着一个帧在 栈中的入栈至出栈的过
程。在后文中,我们将着重讨论 栈中本地变量表部分。
经常有人把 内存简单的区分为堆内存())和栈内存
('),实际中的区域远比这种观点复杂,这样划分只是说明与变量定
义密切相关的内存区域是这两块。其中所指的“堆”后面会专门描述,而所指
的“栈”就是 栈中各个帧的本地变量表部分。本地变量表存放了编译期可
知的各种标量类型
(*、*#、&、&、、+、、*)、对象
引用(不是对象本身,仅仅是一个引用指针)、方法返回地址等。其中
和 * 会占用 % 个本地变量空间(,%*),其余占用 个。本地
变量表在进入方法时进行分配,当进入一个方法时,这个方法需要在帧中
分配多大的本地变量是一件完全确定的事情,在方法运行期间不改变本地
变量表的大小。
在 中对这个区域规定了 % 中异常状况:如果线程请求的栈深度大
于虚拟机所允许的深度,将抛出 '"+$ 异常;如果 栈
可以动态扩展( 中允许固定长度的 栈),当扩展时无法申请
到足够内存则抛出 ""#$ 异常。
,本地方法栈( &')
本地方法栈与 栈所发挥作用是类似的,只不过 栈为虚拟机运行
原语服务,而本地方法栈是为虚拟机使用到的 方法服务。它的实现
的语言、方式与结构并没有强制规定,甚至有的虚拟机(譬如
) 虚拟机)直接就把本地方法栈和 栈合二为一。和 栈一样,
这个区域也会抛出 '"+$ 和 ""#$ 异常。
- 堆())
对于绝大多数应用来说, 堆是虚拟机管理最大的一块内存。 堆是
被所有线程共享的,在虚拟机启动时创建。 堆的唯一目的就是存放对
象实例,绝大部分的对象实例都在这里分配。这一点在 中的描述
是:所有的实例以及数组都在堆上分配(原文:.&&&
&&#
#),但是在逃逸分析和标量替换优化技术出现后,
的描述就显得并不那么准确了。
堆内还有更细致的划分:新生代、老年代,再细致一点的:
、、,甚至更细粒度的本地线程分配缓冲
(./01)等,无论对 堆如何划分,目的都是为了更好的回收内存,
或者更快的分配内存,在本章中我们仅仅针对内存区域的作用进行讨论,
堆中的上述各个区域的细节,可参见本文第二章《 内存管理:深
入垃圾收集器与内存分配策略》。
根据 的要求, 堆可以处于物理上不连续的内存空间,它逻
辑上是连续的即可,就像我们的磁盘空间一样。实现时可以选择实现成固
定大小的,也可以是可扩展的,不过当前所有商业的虚拟机都是按照可扩
展来实现的(通过234 和23 控制)。如果在堆中无法分配内存,并且
堆也无法再扩展时,将会抛出 ""#$ 异常。
5方法区(&0)
叫“方法区”可能认识它的人还不太多,如果叫永久代(
6)它的粉丝也许就多了。它还有个别名叫做 2)(非
堆),但是 上则描述方法区为堆的一个逻辑部分(原文:&
&#&&),这个名字的问题还真容
易令人产生误解,我们在这里就不纠结了。
方法区中存放了每个 的结构信息,包括常量池、字段描述、方法描
述等等。 描述中对这个区域的限制非常宽松,除了和 堆一
样不需要连续的内存,也可以选择固定大小或者可扩展外,甚至可以选择
不实现垃圾收集。相对来说,垃圾收集行为在这个区域是相对比较少发生
的,但并不是某些描述那样永久代不会发生 6(至少对当前主流的商业
实现来说是如此),这里的 6 主要是对常量池的回收和对类的卸载,
虽然回收的“成绩”一般也比较差强人意,尤其是类卸载,条件相当苛刻。
7运行时常量池()
文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一
项信息是常量表89*:,用于存放编译期已可知的常量,
这部分内容将在类加载后进入方法区(永久代)存放。但是 语言并不
要求常量一定只有编译期预置入 的常量表的内容才能进入方法区常
量池,运行期间也可将新内容放入常量池(最典型的 8:方
法)。
运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池
无法在申请到内存时会抛出 ""#$ 异常。
;本机直接内存(<#)
直接内存并不是虚拟机运行时数据区的一部分,它根本就是本机内存而不
是 直接管理的区域。但是这部分内存也会导致 ""#$ 异
常出现,因此我们放到这里一起描述。
在 <=- 中新加入了 >" 类,引入一种基于渠道与缓冲区的 >" 方式,它
可以通过本机 函数库直接分配本机内存,然后通过一个存储在
堆里面的 <1#1? 对象作为这块内存的引用进行操作。这
样能在一些场景中显著提高性能,因为避免了在 对和本机堆中来回复
制数据。
显然本机直接内存的分配不会受到 堆大小的限制,但是即然是内存那
肯定还是要受到本机物理内存(包括 @0 区或者 @ 虚拟内存)
的限制的,一般服务器管理员配置 参数时,会根据实际内存设置234
等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内
存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现
""#$ 异常。
实战 OutOfMemoryError
上述区域中,除了程序计数器,其他在 中都描述了产生
""#$(下称 "")的情形,那我们就实战模拟一下,通过几
段简单的代码,令对应的区域产生 "" 异常以便加深认识,同时初步介绍一
些与内存相关的虚拟机参数。下文的代码都是基于 ) 虚拟机 7 版
的实现,对于不同公司的不同版本的虚拟机,参数与程序运行结果可能结果会
有所差别。
Java 堆
堆存放的是对象实例,因此只要不断建立对象,并且保证 6
到对象之间有可达路径即可产生 "" 异常。测试中限制 堆大小为
%A,不可扩展,通过参数233B)<"""#$ 让虚
拟机在出现 "" 异常的时候 < 出内存映像以便分析。(关于 < 映
像文件分析方面的内容,可参见本文第三章《 内存管理:深入 内存异
常分析与调优》。)
清单 : 堆 "" 测试
CC
C0:23%A234%A233B
)<"""#$
CD&EE
C
*)""F
"""*GF
H
*8IJ:F
/K"""*GLM0#/K"""*GL8:N
&8:F
8"""*G8::N
H
H
H
运行结果:
G""#$B&
<&G9,-A-&
)!I%%A-5OP*#A77,J
VM 栈和本地方法栈
剩余27页未读,继续阅读
资源评论
悠闲饭团
- 粉丝: 150
- 资源: 3301
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- JavaScript《基于自动分析数据并给出营业建议的餐厅管理系统(接入AI) 》+源代码+项目说明及资料
- 355670834783295707ad04e-427f-4cde-9589-e578224a8459.zip
- 动态sql解析引擎,类似mybatis动态sql的功能
- EDA365-Skill-V2.5安装包,支持Allegro17.x版本
- C# 常用单词汇总,常用单词汇总
- 【ERP标准流程-标准流程-库内业务管理】(DOC 14页).doc
- Python《数据库期末作业-餐厅点单系统 》+源代码+设计资料
- 学生成绩管理系统(C++课程设计
- 双指针法判断链表有环-go语言实现
- MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功