当前位置: 首页 > article >正文

JVM篇(Java内存区域与内存溢出异常(深入理解JVM第三版))(持续更新迭代)

目录

学习前言

一、内存管理控制

1. C/C++程序员

2. Java程序员

二、运行时数据区(深入理解JVM第三版)

1. 程序计数器

2. Java虚拟机栈

3. 本地方法栈

4. Java堆

5. 方法区

运行时常量池


学习前言

JVM学习预热后,我们涉及到了JVM的自动内存管理、类加载机制、字节码这些.

只不过我们只是简单地介绍了下,接下来,就让我们来看看JVM的自动内存管理知识吧,我们肯定

还是先看书吧,以下就是来自于深入理解JVM第三版这一书籍的某章节内容总结!

一、内存管理控制

1. C/C++程序员

每一个对象生命从开始到终结的维护责任由C/C++程序员自己完成,容易出现内存泄漏和内存溢出问题。

2. Java程序员

在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,正是因为Java程

序员把控制内存的权力交给了Java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内

存的,那排查错误、修正问题将会成为一项异常艰难的工作。

二、运行时数据区(深入理解JVM第三版)

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

以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而

建立和销毁。

Java虚拟机所管理的内存将会包括以下几个运行时数据区域

Java虚拟机运行时数据区

1. 程序计数器

程序计数器(Program Counter Register)就是一块较小的内存空间,它可以看作是当前线程所执行的字节

码的行号指示器。

在Java虚拟机的概念模型中,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节

码指令

它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时

刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。

因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器各条线程之间计

数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;

如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。

此内存区域没有规定任何OutOfMemoryError情况的区域。

2. Java虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相

同。虚拟机栈描述的是Java方法执行的线程内存模型

每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数

栈、动态连接、方法出口等信息。

每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

这块内存区域规定了两类异常状况

  1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
  2. 如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryErro异常。

3. 本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟

机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和

OutOfMemoryError异常。

4. Java堆

对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。

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

此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。

从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现

“新生代”“老年代”“永久代”“Eden空间”“From Survivor空间”“To Survivor空间”等名词,说明的是

这些区域划分仅仅是一部分垃圾收集器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存

布局,更不是《Java虚拟机规范》里对Java堆的进一步细致划分。

不少资料上经常写着类似于“Java虚拟机的堆内存分为新生代、老年代、永久代、Eden、Survivor……”这样

的内容。

在十年之前(以G1收集器的出现为分界),作为业界绝对主流的HotSpot虚拟机,它内部的垃圾收集器全部

都基于“经典分代”来设计,需要新生代、老年代收集器搭配才能工作,在这种背景下,上述说法还算是不会产生

太大歧义。

但是到了今天,垃圾收集器技术与十年前已不可同日而语,HotSpot里面也出现了不采用分代设计的新垃圾

收集器,再按照上面的提法就有很多需要商榷的地方了。

从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local

Allocation Buffer,TLAB),以提升对象分配时的效率。

不过无论从什么角度,无论如何划分,都不会改变Java堆中存储内容的共性,无论是哪个区域,存储的都只

能是对象的实例,将Java堆细分的目的只是为了更好地回收内存,或者更快地分配内存。

根据《Java虚拟机规范》的规定,Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连

续的,这点就像我们用磁盘空间去存储文件一样,并不要求每个文件都连续存放。

但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续

的内存空间。

Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现

的(通过参数-Xmx和-Xms设定)。

如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError

异常。

5. 方法区

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信

息、常量、静态变量、即时编译器编译后的代码缓存等数据。

在JDK 8以前,把方法区称呼为“永久代”(PermanentGeneration)永久代有-XX:MaxPermSize的上

限,即使不设置也有默认大小,而到了JDK 8,完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中

实现的元空间Metaspace)来代替,

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。


http://www.kler.cn/news/350723.html

相关文章:

  • 利用移动式三维扫描技术创建考古文物的彩色纹理网格【上海沪敖3D】
  • 基于HEC-Ras及ArcGIS的泥石流数值模拟与灾害风险评估典型案例
  • 【逆向基础】十七、PE文件格式(二)
  • ANSI C、ISO C、POSIX标准、GNU的含义
  • 《跟我一起学“Harmony-ArkTS”》——TypeScript基础语法
  • V2X介绍
  • LabVIEW提高开发效率技巧----图像处理加速
  • C语言 | Leetcode C语言题解之第485题最大连续1的个数
  • 9-单引号和双引号的区别在PHP
  • Python库numpy之三
  • Clip模型详解
  • WebSocket在建立连接时,确实也需要进行TCP的三次握手
  • 【深度学习代码调试1】环境配置篇(上) -- 安装PyTorch(安利方法:移除所有国内源,使用默认源)
  • 大模型量化算法之Smoothquant
  • GCC(GNU Compiler Collection)部分参数解释
  • 网站设计之网站设计总结
  • 深度学习:模型攻击(Model Attack)详解
  • 计算机网络——应用层(DNS域名系统、文件传输协议FTP、远程终端协议TELNET、万维网)
  • 数据库->库的操作
  • CCleaner Pro v6.29.11342 系统清理优化软件绿色便携版
  • 如何匿名浏览网站,保护在线隐私?
  • Canmv k230 C++案例1.2——image classify项目 C++代码分析(待完成)
  • 网络安全公司及其主要产品介绍
  • Django CORS跨域支持
  • 基于Python的博客系统
  • Android10 recent键相关总结