51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1601|回复: 3
打印 上一主题 下一主题

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

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-4-26 15:59:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

Java内存区域与内存溢出异常
1.概述

对于 Java 的开发者来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个 new 操作去写配对的
delete / free 代码,这样不容易出现内存泄露和内存溢出的问题,只要全权交给虚拟机去处理。不过,也正是
因为这样,一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会
异常艰难。
所以我们只有了解了虚拟机的各个区域、各个区域的作用、服务对象等,才能在遇到内存问题时去解决这些
问题。
2.运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各
自的用途,以及创建和销毁时间。根据 《Java 虚拟机规范》的规定,Java 虚拟机所管理的内存将会包括以
下几个运行时数据区域。

2.1 - 程序计数器(Program Counter Register)

概述:该区域是一块较小的内存空间,它可以看作是当前线程所执行的字节码的 行号指示器。
作用:通过改变计数器的值来选取下一条需要执行的字节码指令。(分支、循环、跳转、异常处理、线程恢
复等)基础功能都依赖与其完成。
特点:
1.线程私有:因为 Java 虚拟机的多线程是通过 线程轮流切换 并 分配处理器执行时间 来实现的,在某一时
刻,只会执行一条线程。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程
序计数器。
2.无内存溢出:如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在 执行的虚拟机字节码指令
的地址;如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在 Ja
va 虚拟机程序规范中没有规定任何 OutOfMemoryError 情况的区域。
2.2 - Java 虚拟机栈(Java Virtual Machine Stacks)

2.2.1 - Java 虚拟机栈

概述:描述 Java 方法执行的内存模型,每个方法从调用直至执行的过程,对应着一个 栈帧 在虚拟机栈中入
栈到出栈的过程。
作用:存储局部变量表、操作数栈、动态链接、方法出口等信息。
特点:
1.线程私有。
2.生命周期与线程相同。
2.2.2 - 局部变量表

概述:存放了编译期间可知的各种基本数据类型(8种)、对象引用、returnAddress 类型(指向一条字节码指
令的地址)。
占用空间:64位长度的 long 和 double 类型占用 2 个局部变量空间(Slot),其余数据类型只占用 1 个。
分配时机:在编译期间完成分配,当进入一个方法时,这个方法所需要在帧中分配多大的局部变量空间是完全
确定的,在方法运行期间不会改变局部变量表的大小。
我们经常将 Java 内存分为堆内存(Heap)和栈内存(Stack),这种分法中所指的栈就是 Java 虚拟机栈,或
者说是虚拟机栈中 局部变量表 部分。
2.2.3 - 对象引用

概述:reference 类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代
表对象的句柄或其他与此对象相关的位置。
2.2.4 - 异常

异常类型        发生条件
StackOverflowError        线程请求的栈深度大于虚拟机所允许的深度时抛出该异常。
OutOfMemoryError        无法申请到足够的内存时抛出该异常。
2.3 - 本地方法栈(Native Method Stack)

概述:与虚拟机栈类似,是为虚拟机使用到的 Native 方法服务的内存区域。
区别:
虚拟机栈:为虚拟机执行 Java 方法(字节码)服务。
本地方法栈:为虚拟机使用到的 Native 方法服务。
异常:与虚拟机栈一致。
在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可
以自由实现它。甚至有的虚拟机(e.g. Sun HotSpot VM)直接将本地方法栈与虚拟机栈合二为一。
2.4 - Java 堆(Java Heap)

概述:对于大多数应用来说,该区域是 Java 虚拟机所管理的内存中最大的一块区域。
作用:此区域唯一的目的就是存放对象实例。
特点:
1.被所有线程共享。
2.在虚拟机启动时创建。
异常
异常类型        发生条件
StackOverflowError        无
OutOfMemoryError        在堆中没有内存来完成实例分配,且堆无法再扩展时,抛出该异常。
内存:Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。在实现时,既可以实现成固
定大小的,也可以是可扩展的,当前主流的虚拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)。
Reminde 🤗
随着 JIT 编译器的发展与逃逸分析技术成熟,栈上分配、标量替换 等优化技术将会导致一些微妙的变化发生,
所有的对象都分配在堆上也变得不那么绝对了。
2.5 - 方法区(Method Area)

概述:Java 虚拟机规范将方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目
的是与 Java 堆区分开。
作用:存储已被虚拟机加载的(类信息、常量、静态变量、即时编译器编译后的代码)等数据。
特点:线程共享。
异常
异常类型        发生条件
StackOverflowError        无
OutOfMemoryError        当方法区无法满足内存分配需求时,抛出该异常。
内存:Java 虚拟机规范对方法区的限制非常宽松,除了和 Java 堆一样不需要连续的内存空间和可以选择固定
大小或者可扩展外,可以选择不实现垃圾收集。
相对而言,垃圾收集行为在这个区域是比较少出现的,这个区域的内存回收目标主要是针对 常量池的回收 和
类型的卸载。
2.6 - 运行时常量池(Runtime Constant Pool)

概述:方法区的一部分。Class 文件中除了有类的(版本、字段、方法、接口)等描述信息外,还有一项信息就
是常量池。
作用:用于存放编译器生成的各种 字面量 和 符号引用。
动态性:Java 语言并不要求常量池一定只有编译期才能产生,也就是并非预置入 Class 文件中常量池的内容后才
能进入方法区的运行时常量池,运行期间也可以将新的常量放入池中,这种特性用的比较广泛的便是 String 类的
intern() 方法。
异常
异常类型        发生条件
StackOverflowError        无
OutOfMemoryError        因为是方法区的一部分,所以受到方法区内存的限制,当常量池无法再申请到内存时抛出该异常。
Java 虚拟机对 Class 文件的每一部分(包括常量池)的格式都有严格规定,每一个字节用于存储哪种数据类型
必须符合规范上的要求才会被虚拟机认可、装载和执行,但对于运行时常量池,Java 虚拟机规范没有做任何细节
的要求,不同的提供商实现虚拟机可以按照自己的需求来实现这个内存区域。一般来说,除了保存 Class 文件中
描述的符号引用外,还会把翻译出来的直接引用也存储在该区域中。
2.7 - 直接内存(Direct Memory)

概述:并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。但是这部分内存也被
频繁地使用,而且也可能导致 OutOfMemoryError 异常出现。
作用:在 JDK1.4 中新加入了 NIO(New Input/Output) 类,引入了一种基于通道(Channel)与缓冲区(Buffer)
的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer
对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来
回复制数据。
异常
异常类型        发生条件
StackOverflowError        无
OutOfMemoryError        受到物理内存限制,动态扩展时无法申请到内存时抛出该异常。
显然,本机直接内存的分配不会受到 Java 堆大小的限制,但是,既然是内存,肯定还是会受到本机内存(包括
RAM 以及 SWAP 区或者分页大小)大小以及处理器寻址空间的限制,当各个内存区域总和大于物理内存限制
(包括物理和操作系统级的限制)时会出现异常。
3.HotSpot 虚拟机对象探秘

这一部分内容将以 HotSpot 虚拟机和常用的内存区域 Java 堆为例,阐述对象分配、布局和访问的全过程。
3.1 - 对象的创建

概述:Java 是一门面向对象的编程语言,在 Java 程序运行过程中无时无刻都有对象被创建出来。在语言层
面上,创建对象通常仅仅是一个 new 关键字而已,而在虚拟机中对象的创建则分为以下几个步骤。
3.1.1 - 类加载

概述:虚拟机遇到一条 new 指令时,首先将去检查指令参数是否能在常量池中定位到一个类的符号引用,
并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
3.1.2 - 分配内存

概述:在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便
可以完全确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /2 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-6-6 04:18 , Processed in 0.065978 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表