JVM—内存管理(运行时数据区)、垃圾回收

news2024/10/5 22:22:18

背景介绍

当JVM类加载器加载完字节码文件之后,会交给执行引擎执行,在执行的过程中会有一块JVM内存区域来存放程序运行过程中的数据,也就是我们图中放的运行时数据区,那这一块运行时数据区究竟帮我们做了哪些工作?我们常说的线上内存泄漏和内存溢出是因为什么?我们今儿来揭开看看它神秘的面纱。
摘自网上


过程

在讲述运行时数据区有哪些部分之前先讨论以下对象的创建流程

一、对象创建流程

在这里插入图片描述

在上一篇文章中已经讲述了类加载的过程,虚拟机需要给new的新对象分配内存空间,重点来说说它的下一步——分配内存

在分配内存的时候有两种方式:

1、指针碰撞

假设Java堆内存规整,所有的内存占用是连续空间,通过一个指针将已经使用和未使用的空间隔开,指针作为临界。

2、空闲列表

假设Java堆内存不规整,内存占用是零散的,此时JVM通过一个空闲列表(Free List)维护空闲内存信息,里面记录了哪些内存空间是可用的。再次分配新对象的时候直接从空闲列表中找哪块空间可以使用进行分配即可

但是在并发情况下可能会出现线程安全的问题,对象1和对象2同时拿到指针,对象1在分配完内存空间之后对象2也会分配内存空间,对象2就会把对象1的空间覆盖,导致数据被覆盖。那怎么解决这个问题呢?

两种方案解决线程不安全:

1、CAS:自旋锁不断重试

2、TLAB:在堆内存给每个线程预先分配一块内存,线程中每次要开辟空间就在预分配的内存中开辟



下面就进入我们的正题——内存管理
在这里插入图片描述

  • 线程共有:堆、方法区
  • 线程私有:程序计数器、Java虚拟机栈、本地方法栈


二、内存管理

1、 程序计数器(PC)

上官方百度百科介绍:
在这里插入图片描述
作用:存放当前线程执行的下一条指令地址。
在多线程环境下线程之间会涉及到线程切换问题,为了保证线程切换之后还能继续按照上一次切换的位置继续执行,PC会进行指令的记录。PC是线程独有的,不可共享



2、Java虚拟机栈

作用:

每个线程在创建时都会创建一个虚拟机栈,保存了一个一个的栈帧 。针对栈帧我们来具体说说,下图为Java虚拟机中栈帧的内部结构,每执行一个方法都会创建一个栈帧(一个方法对应一个栈帧) ,而栈帧中包含了四部分:局部变量表、操作数栈、方法返回地址、动态链接,而每一个栈帧执行的过程也是入栈、出栈的过程。
在这里插入图片描述

包括:

  • 操作数栈(Operand Stack):用来在执行字节码指令过程中用来计算的
  • 局部变量表(LocalVariables):在方法执行过程中实时记录每个局部变量对应的值
  • 方法返回地址(Return Address):地址
  • 动态链接(Dynamic Linking):符号引用转换为调用方法的直接引用

特点:

  • 线程私有
  • 遵守栈FIFO规则,方法开始执行栈帧入栈,方法执行完栈帧弹出,所以虚拟机不需要垃圾回收

存在的问题:

  • 如果线程太多了,但是没有足够空间创建虚拟机栈,会发生栈溢出
  • 方法调用层次太多,可能出现StackOverflowError


3、本地方法栈(Native Method Stacks)

作用:存储Native方法


4、方法区(Method Area)

作用:
存储被Java虚拟机加载过后的class类信息、常量、静态变量、编译后的代码
不知道大家是否还记得上一篇分享中讲到的类加载过程,其中加载这一步会通过类全限定名加载成class类对象,其中就会把class信息、静态变量、常量等信息加载到方法区,看下面这张图
在这里插入图片描述


5、堆(Heap)

在这里插入图片描述

所有线程共享的一块内存区域,在new对象的时候会在Heap分配内存空间。可以细分为:

  • 年轻代和老年代,对应的比例为2:1 ,新生代存放朝生夕死的对象,老年代存放生命周期较长的对象
  • 年轻代又可以分为Eden、From Survivor、ToSurvivor三个区,对应的比例为:8:1:1(默认情况下,可以通过-XX:SurvivorRatio来调整)

那三个区域中对象是如何进行流转的呢?我们具体来看一下

1、在最开始讲述对象创建流程中包含了一步是分配内存,就是通过指针碰撞或空间散列在Heap中分配内存,新new对象的对象JVM会默认优先分配在Eden区,当Eden区空间逐渐减少(可以默认配置Eden空间容量)的时候,就会触发Young GC来清理,Eden区对象就会放入Survivor区;
2、Survivor区每次分配内存只使用其中一块,Eden和Survivor存活对象会复制到另一块Survivor区中,Eden和原来的Survivor区对象会被清理掉。(这也是为什么图中我只在其中一个Survivor区画了对象,另一块Survivor区没画的原因
3、在对象头中记录了对象迭代的年龄(年龄计数器),当进入Survivor区开始每YoungGC一次年龄就会+1,当年龄达到15的时候就会进入老年代

从图上我们看到进入老年代的条件远不止年龄>=15这一个,对象会进入老年代的方式共有四个:

  1. 长期存活:对象头中记录了对象迭代的年龄,每次迭代都会—+1,当年龄达到15(默认)
  2. 超大对象:占用大量连续空间
  3. 动态年龄判断:servivor中相同年龄对象的总和>survivor空间一半
  4. 空间分配担保:Young GC后,新生代有大量对象对象存活,需要老年代分配担保

三、垃圾回收

在这里插入图片描述

垃圾回收是什么?

清理不再使用的对象,释放内存空间

为什么要进行垃圾回收?

如果不清理这些垃圾对象,那么它们会一直占用着内存,而不能给其他对象是用,最终垃圾对象越来越多,就会出现OOM

什么样的对象是垃圾?

JVM没有任何引用指向它的对象

如何判断对象是垃圾?

1、引用计数法

每个对象都保存一个引用计数器属性,用户记录对象被引用的次数,每被引用一次计数器值就+1;当引用失效时就-1。当计数器为0则表示是垃圾对象

2、可达性分析

从GC Roots开始,遍历,一层一层的往下级找引用对象,找到的对象就是存活对象,没找到的就是垃圾对象
在这里插入图片描述

垃圾回收的三种方式分别是哪些?

1、标记-清除算法(Mark-Sweep)

标记:标记出未引用对象
清除:回收所有被标记的未引用对象

问题:

  1. 如果堆内包含了大量的对象都是需要被回收,这时会执行大量标记和清除操作,导致执行效率降低
  2. 内存空间碎片化,标记和清除后会产生大量不连续的内存碎片,导致在之后在给对象分配内存空间的时候因为内存不足而再次触发Young GC

2、标记-复制算法(Mark-Copying)

基于标记-清除算法,解决碎片化问题。上文中我也提到了新生代中Survivor分为了From、To两个区域,每次只使用其中一块,当其中一块内存用完了,会将存活的对象复制到另一块区域上,然后清理掉使用的survivor区域,保证内存区域连续可用。

问题:
空间一分为2,利用率低,空间浪费

3、标记-整理(Mark-Compact)

基于标记-清除算法,解决内存碎片和空间浪费问题。将存活的对象向一端移动,清除标记的垃圾对象,保证区域连续可用
问题:
内存变动大,当对象位置移动相应的引用地址也会变动

如何选用使用什么回收算法呢?

分代回收:基于heap各个区域对象生命周期来看,每个区域采用不同的回收算法:

  • 新生代:标记-复制
  • 老年代:标记-清理/标记-整理

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

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

相关文章

【学习FreeRTOS】第6章——FreeRTOS中断管理

【本篇文章的也可参考STM32中断文章http://t.csdn.cn/foF9I,结合着学习效果更好】 1.什么是中断 中断:让CPU打断正常运行的程序,转而去处理紧急的事件(程序),就叫中断中断执行机制,可简单概括…

Nginx配置文件理解

之前除了一篇nginx基础安装和基础使用的文章,由于长时间不使用nginx 了,再写一篇文章加强一下对nginx 的理解;当然更深入细致的理解最好去官网细致学习一下,并配和实践多多练习才是最好的; nginx常用的特性&#xff1a…

使用GUI Guider工具在MCU上开发嵌入式GUI应用 (1) - GUI Guider简介及安装

使用GUI Guider工具在MCU上开发嵌入式GUI应用 (1) - GUI Guider简介及安装 受限于每篇文章最多只能贴9张图的限制,这个教程被拆分成了多篇文章连载发布,完整目录结构如下图x所示。后续会发布完整教程的pdf文件,敬请期待。 图x 完整教程文档…

QGIS二次开发六:VS不借助QT插件创建UI界面

上一篇博客我们说了在VS中如何使用QT插件来创建UI界面,但是我们二次开发QGIS的第一篇博客就说了,最好使用OSGeo4W中自动下载的QT进行QGIS二次开发,这样兼容性是最好的,那么该如何在VS中不使用外部安装的QT以及QT的VS插件情况下进行…

解决ElementUI动态表单校验验证不通过

这里记录一下&#xff0c;写项目时遇到的一个问题&#xff1a;就是动态渲染的表单项&#xff0c;加验证规则后一直不通过&#xff01;&#xff01;&#xff01; 原代码 html部分&#xff1a; <el-form-itemv-for"(teaclass,index) in addFom.classIds":label&quo…

Rust 编程小技巧摘选(8)

目录 Rust 编程小技巧(8) 1. 取整函数 floor() 2. 取整函数ceil() 3. 取整函数 round() 4. 保留小数位数 5. 字符串转整数 unwrap() unwrap_or() Rust 编程小技巧(8) 1. 取整函数 floor() floor函数对浮点数进行向下取整 示例代码&#xff1a; fn main() {let x: …

基于概率神经网络的变压器故障诊断

1.案例背景 1.1 PNN概述 概率神经网络(probabilistic neural networks. PNN)是 D.F.Specht博士在1989年首先提出的,是一种基于Bayes分类规则与Parzen窗的概率密度函数估计方法发展而来的并行算法。它是一类结构简单、训练简洁,应用广泛的人工神经网络。在实际应用中,尤其是在解…

【Linux】网络通信

【Linux】网络通信 文章目录 【Linux】网络通信1、网络基础1.1 计算机网络1.2 网络模型TCP & UDP1&#xff09;IP地址2&#xff09;端口3&#xff09;TCP协议与UDP协议的比较 1.3 网络传输1.3.1 传输逻辑1.3.2 传输条件1.3.3 传输流程 1.4 地址管理 2、网络编程2.1 基本概念…

Django项目局域网访问

1、需求 主机运行着Django项目&#xff0c;想要被局域网其它设备访问。 2、解决步骤&#xff08;非常简单&#xff09; 查看本机局域网ip&#xff0c;如&#xff1a;192.168.100.100运行项目&#xff1a;python manage.py runserver 192.168.100.100:8080。 注意这里的地址很…

python+tkinter实现图书管理系统(首发)

文章目录 前文运行环境功能图数据操作图书数据管理用户数据管理借书记录管理 功能界面管理员界面首页图书管理用户管理借书记录更改密码 普通用户界面 其他功能数字时间显示加载画面显示输入框提示词界面居中显示借书时间和还书时间记录公告栏数据操作 结尾 前文 本文将用tkin…

Nuitka实战

安装Nuitka pip install -U nuitka 安装好之后查看版本 python -m nuitka --version 显示gcc版本太低&#xff0c;与nuitka不兼容&#xff0c;所以我们要升级gcc版本 升级之前&#xff0c;先查看一下gcc版本信息 gcc --version 可以看到&#xff0c;Centos 7.7默认gcc版本为…

SRE之前端服务器的负载均衡

写在前面 今天和小伙伴们分享一些前端服务器的负载均衡技术内容为结合《 SRE Google运维解密》 整理&#xff1a; 涉及DNS 负载均衡VIP 负载均衡反向代理负载均衡 理解不足小伙伴帮忙指正 傍晚时分&#xff0c;你坐在屋檐下&#xff0c;看着天慢慢地黑下去&#xff0c;心里寂寞…

人大金仓三大兼容:Oracle迁移无忧

企业级应用早期的架构模式是C/S&#xff08;Client/Server&#xff09;模式&#xff0c;Client做人机交互逻辑的呈现&#xff0c;Sever做业务计算逻辑的实现。这就类似餐馆的运作模式&#xff0c;Client是前台的服务员提供点菜和上菜服务&#xff0c;而Server则是后厨完成菜品的…

Java基础篇--修饰符

Java语言提供了很多修饰符&#xff0c;主要分为以下两类&#xff1a; 访问控制修饰符 非访问修饰符 访问控制修饰符 private&#xff1a;私有访问权限&#xff0c;用于修饰类的属性和方法。被private修饰的成员只能在本类中进行访问。default&#xff08;默认访问权限&…

spark 图计算 助力解决 dataframe中的链式依赖

链式依赖说明 name newName a b c d b c 我们需要的结果 即我们可以支持获取到链式转换的 起点 重点 以及链式的中间转换过程顺序数组. 特别说明: 出版只支持 单向 无分叉的图,其他复杂场景暂时未测试. 场景举例: 比如某件商品价格变化,我们需要知…

原始套接字编程(AF_PACKET+SOCK_RAW)模拟一个PING

1. 背景 最近看一个客户的代码片段&#xff0c;发现他在用原始套接字编程&#xff0c;一般学习套接字都是流式套接字和数据报套接字&#xff0c;本来也不是搞网络的&#xff0c;原始套接字了解得很少&#xff0c;借着这次机会&#xff0c;自己来学习一下原始套接字编程。 2. …

函数的模拟实现

题一&#xff1a; 模拟实现strncpy #include <stdio.h>void my_strncpy(char* arr2, char* arr1, size_t num){int i 0;for (i 0; i < num; i){*(arr2 i) *(arr1 i);}}int main(){char arr1[] "hello liangzai";char arr2[10] { 0 };//strncpy(ar…

包管理工具详解npm 、 yarn 、 cnpm 、 npx 、 pnpm(2023)

1、包管理工具npm &#xff08;1&#xff09;包管理工具npm&#xff1a; Node Package Manager&#xff0c;也就是Node包管理器&#xff1b;但是目前已经不仅仅是Node包管理器了&#xff0c;在前端项目中我们也在使用它来管理依赖的包&#xff1b;比如vue、vue-router、vuex、…

3.6 Spring MVC文件上传

1. 文件上传到本地 实现方式 Spring MVC使用commons-fileupload实现文件上传&#xff0c;注意事项如下&#xff1a; l HTTP请求方法是POST。 l HTTP请求头的Content-Type是multipart/form-data。 SpringMVC配置 配置commons-fileupload插件的文件上传解析器CommonsMultip…