我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java内存分配与回收机制是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java内存分配与回收机制是什么

本篇内容介绍了“Java内存分配与回收机制是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一.运行时数据区域

下图是Java虚拟机运行时的内存示意图:

Java内存分配与回收机制是什么

从图中我们可以看到Java内存总共分为6个部分:

  1. 程序计数器:每条线程都有一个独立的程序计数器,计数器可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时,就是通过改变这个计数器的值来选取下一条所需执行的字节码指令、分支、循环、跳转、异常处理,线程恢复等基础功能都需要依赖这个计数器完成。

  • Java虚拟机栈:虚拟机栈是线程私有的,生命周期与线程相同。虚拟机栈为Java方法执行描述内存模型,每个方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。

  • 本地方法栈:与虚拟机栈发挥的作用相似。区别是虚拟机栈为执行Java方法服务,本地方法栈为Native方法服务。

  • 堆:所有线程共享的区域。在虚拟机启动时创建,所有的对象实例几乎都在堆上分配。Java堆还可以细分为:新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间。不过无论如何划分,存储的都是对象实例,进一步划分的目的是为了更好的回收内存,或者更快的分配内存。

  • 方法区:方法区是各个线程共享的内存区域,主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。这块区域与Java堆一样不需要连续的内存和可以选择固定大小或可扩展外,还可以选择不实现垃圾收集。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,垃圾收集行为在这个区域较少出现。

  • 运行时常量池:运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面符和符号引用,这部分内容在类加载后进入方法区的运行时常量池中存放。

  • 直接内存:直接内存也称堆外内存,它不是虚拟机运行时数据区的一部分。JDK1.4后引入NIO类,是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接在堆外分配内存,然后通过存储在Java堆中的DirectByteBuffer对象作为引用对这块内存进行操作。这样能够显著提高性能,避免Java堆和Native堆中来回复制数据。

所以通过表格的形式概括如下:

数据区域 概括 线程共享 程序计数器 当前线程所执行的字节码的行号指示器 否 虚拟机栈 为Java方法执行创建栈帧存储局部变量、操作数栈、动态链接、方法出口等信息 否 本地方法栈 与虚拟机栈类似,为Native方法服务 否 堆 存放对象实例 是 方法区 存储虚拟机已加载的类信息、常量、静态变量、即时编译后的代码等数据 是 运行时常量池 方法区的一部分,存放编译期生成的字面量和符号引用 是 直接内存 被分配在堆外的内存,性能高,不受Java堆的大小限制 是 二.对象的创建与内存布局

对象的创建

Java内存分配与回收机制是什么

Java对象的创建

上图是对象创建的完整流程图,接下来做详细说明。

  1. 当虚拟机收到new指令后,检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用所代表的类是否已被加载、解析和初始化过。如果没有,必须先执行类加载过程。

  • 在类加载完成后可以确定对象分配所需要的空间。如果Java堆中内存是绝对规整的,用过的内存放一边,空闲的内存放另一边,中间放着一个指针作为分界点的指示器,那分配内存就只是把指针向空闲空间方向挪动一段与对象大小相等的距离,这种分配方式称为"指针碰撞"。如果Java堆中内存不是规整的,空闲内存与使用过的内存是相互交错的,虚拟机必须维护一个列表,记录哪些内存块是可用的,在分配的时候从列表中找出足够的空间分配给对象实例,并更新列表上的记录,这种分配方式称为"空闲列表"。采用哪种分配方式通常由虚拟机的垃圾收集器是否带有压缩整理功能决定。

  • 划分可用空间时,还需考虑为对象实例分配空间时是否是线程安全的。要保证线程安全,有两种方案。一种是对分配内存空间的动作进行同步处理,实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。另一种是把内存分配的动作按照线程划分在不同空间中进行,每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer , TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

  • 内存分配完成后,虚拟机对分配到的内存空间都初始化为零值(不包括对象头),保证对象的实例字段在Java代码中可以不赋初始值就可以直接使用。

  • 虚拟机将对象的信息放入对象的对象头中。

  • 执行构造函数

对象的内存布局

Java内存分配与回收机制是什么

对象的内存布局总共分为三个部分:

  1. 对象头中主要包括两部分信息:

  • 一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

  • 另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是Java数组,那在对象头中还必须有一块记录数组长的数据。

  1. 实例数据部分是对象真正存储的有效信息,也是程序代码中定义的各种类型的字段内容。从父类继承下来的,在子类中定义的都需要记录下来。

  2. 对齐填充仅仅起到占位符的作用。HotSpot VM的自动内存管理系统要求对象起始地址是8字节的整数倍,所以对象大小必须是8字节的整数倍。当对象实例数据部分没有对齐时,需要通过对齐填充来补

在此我向大家推荐一个Java高级群 :725633148 里面会分享一些资深架构师录制的视频录像:(有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构、面试资料)等这些成为架构师必备的知识体系 进群马上免费领取,目前受益良多!

三.内存的回收

对象存活判定

Java虚拟机通过可达性分析来判定对象是否存活。这个算法的基本思想是通过一系列称为"GC Roots"的对象作为起始点,从这些节点向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有与任何引用链相连时,则该对象是不可用的。

如图,object5,object6,object7虽然互有关联,但是GC Roots是不可达的,所以它们被判定是可回收的对象。

另外值得一提的是引用计数算法,引用计数法是通过给对象一个引用计数器,每当有一个地方引用它时,计数器值就加一;引用失效时,计数器值就减一;任何时刻计数器为0的对象就是不可能再被使用的。引用计数器效率高、实现简单。但是很难解决对象间相互循环引用的问题,主流Java虚拟机几乎都不再使用引用计数法来管理内存。

Java内存分配与回收机制是什么

可达性分析示意图

即使在可达性分析算法中不可达的对象,也不一定会立即被回收。一个对象被回收,至少要经历两次标记过程。

如果对象在进行可达性分析后没有与GC Roots相连的引用链,那它将会被第一次标记并进行一次筛选。筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或finalize()方法已被虚拟机调用过,虚拟机将这两种情况视为"没有必要执行"。

如果这个对象判定为有必要执行finalize()方法,那么这个对象会放置在F-Queue队列中,稍后由虚拟机自动建立、低优先级的Finalizer线程去执行finalize()方法。GC对F-Queue中的对象进行第二次小规模标记,如果对象重新与引用链上的任何一个对象建立关联,那么第二次标记时它将被移除"即将回收"的集合。否则对象就真的要被回收了。

Java内存分配与回收机制是什么

Finalize方法

方法区回收判定

方法区的回收主要包括两部分内容:废弃常量和无用的类。

  • 废弃常量的回收与回收Java堆中的对象类似。

  • 判断无用的类的条件必须满足三个条件:

  • 该类所有实例已经被回收。

  • 加载该类的ClassLoader已被回收。

  • 该类对应的java.lang.Class对象没有在任何地方被引用,也无法通过反射访问该类。

垃圾收集算法

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

  2. 算法分为"标记"和"清除"两个阶段:首先标记出需要回收的对象,在标记完成后统一回收被标记的对象。它主要不足有两个:一是效率问题,标记和清除两个过程效率都不高。二是空间问题,标记清除后会产生大量不连续内存碎片,碎片太多可能导致要分配较大对象时,无法找到足够的内存空间不得不提前触发一次垃圾收集动作。

Java内存分配与回收机制是什么

  1.  

  2. 标记-清除

  3. 复制算法:

  4. 复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当一块内存用完了,将存活的对象复制到另一块上面,然后把已使用的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法将内存缩小为原来的一半,代价较高。

Java内存分配与回收机制是什么

  1.  

  2. 复制算法

  3. 标记-整理算法(Mark-Compact):

  4. 标记过程与"标记-清除"算法一样,但后续不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

Java内存分配与回收机制是什么

  1.  

  2. 标记-整理算法

分代收集算法

商业虚拟机的垃圾收集都采用分代收集算法,根据对象存活周期将内存划分为几块。Java堆分为新生代和老年代,这样可以根据年代特点采用适当的收集算法。新生代中每次垃圾收集都有大批对象死去,那就选用复制算法。老年代对象存活率高,没有额外空间进行分配担保,适合使用"标记-清理"或"标记-整理"算法来回收。

内存分配与回收策略

  1. 对象优先在Eden分区:

  2. 大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间分配时,虚拟机发起一次Minor GC。GC后对象尝试放入Survivor空间,如果Survivor空间无法放入对象时,只能通过空间分配担保机制提前转移到老年代。

  3. 大对象直接进入老年代:

  4. 大对象指需要大量连续内存空间的Java对象。虚拟机提供-XX:PretenureSizeThreshold参数,如果大于这个设置值对象则直接分配在老年代。这样可以避免新生代中的Eden区及两个Survivor区发生大量内存复制。

  5. 长期存活的对象进入老年代:

  6. 虚拟机会给每个对象定义一个对象年龄计数器。如果对象在Eden出生并且经过一次Minor GC后任然存活,且能够被Survivor容纳,将被移动到Survivor空间中,并且对象年龄设为1.每次Minor GC后对象任然存活在Survivor区中,年龄就加一,当年龄到达-XX:MaxTenuringThreshold参数设定的值时,将会移动到老年代。

  7. 动态年龄判断:

  8. 虚拟机不是永远要求对象的年龄必须达到-XX:MaxTenuringThreshold设定的值才会将对象移动到老年代去。如果Survivor中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

  9. 空间分配担保:

  10. 在Minor GC前,虚拟机会检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果条件成立,那么Minor GC是成立的。如果不成立,虚拟机查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用连续空间是否大于历次移动到老年代对象的平均大小,如果大于,将尝试一次Minor GC。如果小于,或者HandlePromotionFailure设置值不允许冒险,那将进行一次Full GC。

新生代GC(Minor GC):发生在新生代的垃圾收集动作,因为Java对象大多朝生夕死,所以Minor GC非常频繁,回收速度也较快。

老年代GC(Major GC/Full GC):发生在老年代的垃圾收集动作。出现Major GC,经常会伴随至少一次Minor GC。Major GC的速度一般比Minor GC慢10倍以上。

“Java内存分配与回收机制是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java内存分配与回收机制是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java内存分配与回收机制是什么

本篇内容介绍了“Java内存分配与回收机制是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一.运行时数据区域下图是Java虚拟机运行时的
2023-06-03

php内存回收机制是什么

PHP的内存回收机制主要包括两个方面:引用计数和垃圾回收。1. 引用计数:PHP使用引用计数来跟踪内存中对象的引用数量。当一个对象被引用时,它的引用计数加1;当一个对象的引用被释放时,它的引用计数减1。当一个对象的引用计数为0时,表示该对象
2023-08-24

qt内存回收机制是什么

Qt内存回收机制是通过自动垃圾回收(Garbage Collection,GC)和对象树(Object Tree)来实现的。在Qt中,使用了一种基于引用计数的自动垃圾回收机制。每个QObject对象都维护着一个引用计数,当对象被创建时,引用
2023-09-16

Java内存分配与回收策略

本篇内容介绍了“Java内存分配与回收策略”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!内存分配与回收策略Minor GC 和 Full G
2023-06-02

JVM之内存分配和回收机制

本篇主要介绍JVM内存分配和回收策略,内容主要节选自《深入理解java虚拟机》,需要的朋友可以参考下
2023-05-18

详解Java 虚拟机(第⑥篇)——内存分配与回收策略

一、Minor GC 和 Full GCMinor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC
2023-06-05

Java虚拟机中内存分配与回收策略的示例分析

小编给大家分享一下Java虚拟机中内存分配与回收策略的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!内存分配与回收策略Java技术体系的自动内存管理,最根
2023-06-25

python内存回收机制有什么优点

Python的内存回收机制有以下几个优点:自动化:Python的内存回收机制是自动的,程序员无需手动管理内存。Python会在对象不再被引用时自动回收内存,从而避免了内存泄漏和悬空指针等问题。垃圾回收:Python使用了垃圾回收机制,通过引
2023-10-20

Java垃圾回收机制是什么

这篇文章主要介绍“Java垃圾回收机制是什么”,在日常操作中,相信很多人在Java垃圾回收机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java垃圾回收机制是什么”的疑惑有所帮助!接下来,请跟着小编
2023-06-17

PHP 垃圾回收机制与内存管理

php 的内存管理使用垃圾回收机制,它会在运行时自动释放不再使用的内存,基于引用计数原理运作。为了避免内存泄漏,遵循以下最佳实践:销毁不再需要的变量、避免循环引用、使用弱引用。PHP 垃圾回收机制与内存管理PHP 中的内存管理PHP 使
PHP 垃圾回收机制与内存管理
2024-05-03

php内存管理机制与垃圾回收机制的示例分析

这篇文章给大家分享的是有关php内存管理机制与垃圾回收机制的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、内存管理机制先看一段代码:
2023-06-15

C++ 函数内存分配和销毁与垃圾回收机制的比较

c++++ 使用函数内存分配和销毁,即显式管理内存分配和释放,而垃圾回收机制自动处理这些操作,避免内存泄漏但可能降低效率。C++ 函数内存分配与销毁与垃圾回收机制的比较简介内存管理是编程中的一个关键方面。C++ 使用函数内存分配和销毁机
C++ 函数内存分配和销毁与垃圾回收机制的比较
2024-04-22

php内存回收机制怎么掌握

要掌握PHP内存回收机制,可以关注以下几个方面:1. 了解PHP的垃圾回收机制:PHP使用了基于引用计数的垃圾回收机制。当一个变量的引用计数为0时,说明该变量不再被使用,可以被回收。PHP通过在变量赋值、函数调用、函数返回等场景来更新引用计
2023-09-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录