重温《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)》 –– 学习笔记(二)

news2024/11/29 6:33:15

第二部分:自动内存管理机制

第2章:Java内存区域与内存溢出异常

2.1 概述

Java 与 C++ 之间有一堵由内存动态分配和垃圾收集技术围成的高墙。

Java 程序员在 虚拟机自动内存管理机制 的帮助下,无需为每一个 new 操作去写配对的 delete/free 代码,这样就不容易产生内存泄漏和内存溢出问题。但是也带来了一个问题,一旦出现内存泄漏和内存溢出问题,如果不了解虚拟机是如何使用内存的,那排查起来就会比较困难。

2.2 运行时数据区

Java 虚拟机在执行 Java 程序的过程中会将它管理的内存分为几个区域,这些区域就是运行时数据区,分为:方法区,堆,虚拟机栈,本地方法栈,程序计数器,如下图:
在这里插入图片描述

  • 线程私有:虚拟机栈、本地方法栈、程序计数器
  • 线程共享:方法区、堆
2.2.1 程序计数器(Program Counter Register)
  • 是一块儿很小的内存区域
  • 可以将它看成当前线程所执行的字节码的行号指示器
  • 多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的
  • 在一个确定的时间内,一个处理器(如果是多核那就是一个核)只能执行一条线程中的指令
  • 为了保证线程切换回来能够回到到正确的位置,每条线程都需要一个程序计数器,各线程中的程序计数器互不干扰,独立存储,我们将这类内存区域称为“线程私有”的内存。
  • 如果当前线程执行的是一个 Java 方法,那程序计数器记录的就是线程中正在执行的虚拟机字节码指令的地址。如果当前线程执行的是一个Native方法,那程序计数器中记录的是 Null
  • 此区域是唯一一个Java虚拟机规范没有规定任何 OOM 情况的区域
2.2.2 Java 虚拟机栈(Java Virtual Machine Stacks)
  • 与程序计数器一样,此区域的内存都属于“线程私有”内存
  • 是用来描述 Java 方法执行的内存模型
  • 每个方法执行的同时会创建一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口信息等。方法从调用直至执行完成的过程,对应的就是栈帧在Java虚拟机栈中入栈出栈的过程。
  • 经常会有人把 Java 的内存分为堆内存(Heap)和栈内存(Stack),这种分发是极为粗糙的,严格说来这里的堆内存就是下边要讲到的 Java 堆,而栈内存指的是 Java 虚拟机栈,或者说是 Java 虚拟机栈中的局部变量表部分。
  • 局部变量表存放了编译期可知的基本数据类型,对象引用,returnAddress(一条指向字节码指令的地址)
  • 64 位长度的 long 和 double 类型需要占用 2 个局部变量空间(slot),其他类型需要占用1 个
  • 局部变量表的空间分配是在编译期完成的。当进入到一个方法时,该方法在栈帧中需要分配多大的局部变量表是完全确定的,在方法运行期间局部变量表的空间是不会改变的。
  • Java 虚拟规范中规定该区域有两种异常情况:
    1. 线程请求的栈深度超过了虚拟机允许的最大深度,将抛出 StackOverflowError 异常
    2. 虚拟机栈可以动态扩展的话,当扩展的时候无法申请到足够内存,将抛出OutOfMemoryError 异常
2.2.3 本地方法栈(Native Method Stack)
  • 与 Java 虚拟机栈非常相似,区别在于 Java 虚拟机栈是为虚拟机执行Java方法而服务的,而本地方法栈是为虚拟机执行 Native 方法而服务的。
  • Java 虚拟机规范当中并没有对这个区域进行明确规定,所以具体的虚拟机可以自由的去实现它。甚至有些虚拟机实现将 Java 虚拟机栈和本地方法栈合二为一,例如:HotSpot VM
  • 和Java虚拟机栈一样,本地方法栈可能会抛出 StackOverflowError、OutOfMemoryError 异常
2.2.4 Java 堆(Java Heap)
  • 是 Java 虚拟机所管理的最大内存区域
  • 是被所有线程共享的一块儿区域
  • 堆是在虚拟机启动的时候创建的
  • 此内存区域唯一的目的就是存储对象实例,几乎所有的对象都需要在此区域分配内存。这一点在 Java 虚拟机规范当中的描述是:所有的对象实例和数组都要在堆上分配。但是随着技术的发展和更新,例如:JIT编译器的发展、逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术,使所有对象都需要在堆上分配内存变得不那么绝对了
  • 此区域也被称为 GC 堆,主要因为该区域是垃圾收集器管理的主要区域
  • 从内存回收的角度来看,由于现在的圾收集器基本上都采用分代收集的算法,所以Java堆还可以细分为:新生代和老年代。再细分的话,就是Eden空间,From Survivor 空间、To Survivor空间
  • 根据 Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可
  • Java 堆可以是固定的,也可以是可扩展的,当前主流的虚拟机 Java 堆都是可扩展的(通过-Xmx和-Xms参数)
  • 如果在堆中没有内存完成实例分配,并且堆也无法进行扩展了,将会抛出OutOfMemoryError异常
2.2.5 方法区(Method Area)
  • 该区域与Java堆一样,都属于线程共享的内存区域。
  • 用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译过的代码等数据。
  • 在 Java 虚拟机规范中将方法区描述为堆的一个逻辑部分,为了和堆有所区分,方法区别名为 Non-Heap(非堆)。
  • 作为 HotSpot VM 的用户来说,很多人愿意将方法区称为“永久代”,两者实则不等价,仅仅是因为 HotSpot 虚拟机将 GC 分代收集扩展到了方法区,或者说用永久代实现了方法区。这样的好处是,HotSpot 虚拟机垃的圾收集器可以像管理 Java 堆一样来管理方法区,省去了专门为方法区编写内存管理的代码了。但同时也带来了问题,那就是更容易出现OutOfMemoryError异常,因为永久代有 XX:MaxPermSize 的上限。其他虚拟机(例如:JRockit、J9)不存在永久代,只要没达到进程可用内存上线,就不会出现内存溢出的问题。
  • HotSpot 官方也有在未来用 Native Memory 替代永久代来实现方法区的计划。在JDK1.7 的 HotSpot 中,已经把永久代中字符串常量池移出来了。
  • 运行时常量池(Runtime Constant Pool)是方法区的一部分
  • Class文件中有常量池信息(Constant Pool Table),常量池用来存放编译器生成字面量和符号引用,这部分内容将在类加载后进入到方法区的运行时常量池中存放。
  • 运行时常量池相较于Class文件中常量池具有一个动态性的特征,Java语言并没有规定只有在编译期产生常量,也就是说并不是Class文件中的常量池中的内容才能进入到运行时常量池中,运行期间也可能将新常量存放到运行时常量池中,这种特性被开发人员利用较多的是String的intern()方法。
  • 运行时常量池作为方法区的一部分,自然受到方法区内存的限制,当运行时常量池无法申请到内存的时候,则抛出OutOfMemoryError异常。
2.2.6 直接内存(Direct Memory)
  • 直接内存既不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中所规定的内存区域。
  • JDK 1.4 引入了 NIO(New Input/0utput)类,是一种基于管道(Channel)和缓冲区(Buffer)的I/O方式,它通过Native函数库直接对堆外内存进行分配,并通过堆中的一个对象(DirectByteBuffer)作为此块儿内存的引用进行操作。
  • 直接内存分配虽然不受 Java 堆大小的限制,但是既然是内存,还是会受到本机总内存以及处理器寻址空间的限制,当没有做够的空间来分配内存的时候,将会抛出OutOfMemoryError异常。
相关联文章

上一篇:《重温《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)》 –– 学习笔记(一)》
下一篇:整理中…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1432435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TOP100-链表(四)

9.24. 两两交换链表中的节点 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1: 输入:head [1,2…

myql 项目数据库和表的设计

1.表的设计和创建 2.在navicate运行这些代码 create table user(id int not null auto_increment primary key,name varchar(50) not null unique,password varchar(50) not null,state enum(online,offline) default offline ); create table friend(userid int not null,…

java之ReentrantLock

在讲RentrantLock之前需要先讲一下AQS和LockSupport,因为rentrantLock底层是用AQS实现的,而AQS中获取阻塞和唤醒底使用LockSupport实现的。 1、LockSupport实现 下面代码中,LockSupport.park方法是当前线程等待,直到获得许可&am…

06:原生云K8S解密|K8S集群安装部署|K8S网络插件

原生云K8S解密|K8S集群安装部署|K8S网络插件 K8SK8S集群架构图解 K8S部署仓库初始化kube-master安装计算节点的安装token管理 配置flannel网络(master主机操作) K8S 有大量夸主机的容器需要管理,快速部署应用&#xff…

问题:根据全面推进国防和军队现代化的战略安排,_____把人民军队全面建成世界一流军队。 #经验分享#媒体

问题:根据全面推进国防和军队现代化的战略安排,_____把人民军队全面建成世界一流军队。 A、2020年 B、2035年 C、本世纪中叶 D、2045年 参考答案如图所示 问题:判断题:高处作业传递物件应使用绳索,在确认作业下方…

面试数据结构与算法总结分类+leetcode目录【基础版】

🧡🧡🧡算法题目总结: 这里为大家总结数据结构与算法的题库目录,如果已经解释过的题目会标注链接更新,方便查看。 数据结构概览 Array & String 大家对这两类肯定比较清楚的,同时这也是面试…

C#,雅各布斯塔尔—卢卡斯(Jacobsthal Lucas Number)的算法与源代码

1 雅各布斯塔尔序列 雅各布斯塔尔序列是一个与斐波那契序列类似的加法序列,由递归关系JnJn-12Jn-2定义,初始项J00,J11。序列中的一个数字称为雅可布沙尔数。它们是卢卡斯序列Un(P,Q)的一种特殊类型&#x…

【stm32】hal库学习笔记-ADC模数转换(超详细!)

【stm32】hal库学习笔记-ADC模数转换(超详细!) 本篇章介绍了ADC实现电压检测的三种方式 ADC原理及选型 ADC将连续的模拟电压信号转换为二进制的数字信号 选型参数 速度(采样频率) 功耗 精度 转换原理 ADC hal库驱…

redis的缓存击穿和缓存雪崩和缓存穿透问题解决方法

Redis的缓存击穿: 热点的key,在不停的扛着大并发,当这个key失效时,一瞬间大量的请求冲到持久层的数据库中,就像在一堵墙上某个点凿开了一个洞! 解决方法: 1.热点key永不过期: 统计访…

【vue3学习P5-P10】vue3语法;vue响应式实现

0、vue2和vue3对比 框架版本API方式双向绑定原理domFragmentsTree-Shakingvue2选项式API(Options API)基于Object.defineProperty(监听)实现,不能双向绑定对象类型的数据【通过Object.defineProperty里面的set和get做…

【Java基础_02】Java变量

【Java基础_02】Java变量、运算符、程序控制结构 文章目录 1 变量1.1 程序中“”号的使用1.2 数据类型1.3 整数类型1.3.1 整数类型的分类1.3.2 整型的使用细节 1.4 浮点类型1.4.1 浮点型的分类1.4.2 浮点类型使用细节 1.5 字符类型1.5.1 字符类型使用细节1.5.2 字符类型本质1.5…

Mysql学习记录补充

索引 在无索引情况下,就需要从第一行开始扫描,一直扫描到最后一行,我们称之为 全表扫描,性能很低。 如果我们针对于这张表建立了索引,假设索引结构就是二叉树,那么也就意味着,会对age这个字段…

【FX110网】日交所发布1月交易数据:衍生品交易额达历年1月最高!

日本交易所集团(日交所,JPX)发布了其2024年1月的交易数据概览。数据显示,该交易所当月衍生品交易额创新历年来的1月交易数据最高纪录。2024年1月共有19个交易日。 2024年1月交易概览现货股票市场 2024年1月,该交易所主…

ArrayList在添加元素时报错java.lang.ArrayIndexOutOfBoundException

一、添加单个元素数组越界分析 add源码如下 public boolean add(E e) {ensureCapacityInternal(size 1); // Increments modCount!!elementData[size] e;return true; } size字段的定义 The size of the ArrayList (the number of elements it contains). ArrayList的大…

三层交换组网实验(华为)

思科设备参考:三层交换组网实验(思科) 一,技术简介 三层交换技术的出现,解决子网必须依赖路由器进行管理的问题,解决传统路由器低速、复杂所造成的网络瓶颈问题。一个具有三层交换功能的设备可简单理解为…

Unity引擎学习笔记之【角色按键器操作】

角色按键Character Controls 一、脚本操作 设置脚本 设置基本键盘操作 //水平轴float horizontal Input.GetAxis("Horizontal");//垂直轴float vertical Input.GetAxis("Vertical");//创建方向向量Vector3 dir new Vector3(horizontal,0,vertical);/…

《Python 网络爬虫简易速速上手小册》第5章:Python 数据存储与管理(2024 最新版)

文章目录 5.1 选择数据存储方案5.1.1 重点基础知识讲解5.1.2 重点案例:使用 SQLite 存储博客文章数据5.1.3 拓展案例 1:使用 MongoDB 存储社交媒体动态5.1.4 拓展案例 2:使用 Elasticsearch 存储和检索日志数据 5.2 数据清洗与预处理5.2.1 重…

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上,阿里云数据库产品事业部负责人李飞飞在主题演讲中提到,瑶池数据库推出“DB存储”一体化能力,结合人工智能、机器学习、存储等方法和创新能力,实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…

Linux 高并发服务器

多进程并发服务器 使用多进程并发服务器时要考虑以下几点&#xff1a; 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)系统内创建进程个数(与内存大小相关)进程创建过多是否降低整体服务性能(进程调度) server /* server.c */ #include <stdio…

【刷题题解】最长回文子序列

给你一个字符串 s &#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列 这道题&#xff0c;一眼动态规划&#xff0c;但是即使动起来也规划…