深入理解Java虚拟机——内存分配与回收策略

这篇具有很好参考价值的文章主要介绍了深入理解Java虚拟机——内存分配与回收策略。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.前言

在读这篇博客之前,你需要了解分代收集理论中,收集器应该将Java堆划分出不同的区域**,**然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

例如appel式回收,HotSpot虚拟机中的新生代收集器都采用了appel式回收来设计新生代内存布局。

Appel式回收的具体做法是把新生代分为一块较大的Eden空间和两块较小的 Survivor空间,每次分配内存只使用Eden和其中一块Survivor。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1,也即每次新生代中可用内存空间为整个新 生代容量的90%(Eden的80%加上一个Survivor的10%),只有一个Survivor空间,即10%的新生代是会 被“浪费”的。当然,98%的对象可被回收仅仅是“普通场景”下测得的数据,任何人都没有办法百分百 保证每次回收都只有不多于10%的对象存活,因此Appel式回收还有一个充当罕见情况的“逃生门”的安 全设计,当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(实际上大多就是老年代)进行分配担保(Handle Promotion)。

所谓分配担保就是:如果另外一块 Survivor空间没有足够空间存放上一次新生代收集下来的存活对象这些对象便将通过分配担保机制直 接进入老年代这对虚拟机来说就是安全的

深入理解Java虚拟机——内存分配与回收策略

在将Java堆内存划分为不同的区域之后,垃圾收集器才可以每次只回收其中某一个或者某些部分的区域(Minor GC、Major GC、Full GC)。进而演化出与对象存亡特征相匹配的垃圾收集算法。

2.正文

1.对象优先在Eden区分配

大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进分配时,虚拟机将发起一次MinorGC

2.大对象直接进入老年代

大对象指的是需要大量连续内存空间的对象,比如一个很长的字符串或者是一个很大的数组。

大对象对虚拟机的内存分配来说就是一个不折不扣的坏消息,比遇到一个大对象更加坏的消息就是遇到一群“朝生夕灭”的“短命大对象”,我们写程序的时候应注意避免。在Java虚拟机中要避免大对象的原因是,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好它们,而当复制对象时大对象就意味着高额的内存复制开销

HotSpot虚拟机提供了 -XX:PretenureSizeThreshold 参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区之间来回复制,产生大量的内存复制操作。

3.长期存活的对象将进入老年代

在前面我们了解了对象的创建过程中,会设置对象头,其中对象头中就包含了对象的年龄。

对象通常在Eden区里诞生,如果经过第一次 Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并且将其对象年龄设为1岁。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15),就会被晋升到老年代中。

对象晋升老年代的年龄阈值,可以通过参数-XX: MaxTenuringThreshold设置。

4.动态对象年龄判断

为了能更好地适应不同程序的内存状况,HotSpot虚拟机并不是永远要求对象的年龄必须达到 -XX:MaxTenuringThreshold 才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到 -XX:MaxTenuringThreshold 中要求的年龄。

5.空间分配担保

在发生Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次Minor GC可以确保是安全的。如果不成立,则虚拟机会先查看 -XX:HandlePromotionFailure 参数的设置值是否允许担保失败(Handle Promotion Failure)。

-XX:HandlePromotionFailure=true代表允许担保失败;-XX:HandlePromotionFailure=false代表不允许担保失败

如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者 -XX:HandlePromotionFailure 设置不允许冒险,那这时就要改为进行一次Full GC。

深入理解Java虚拟机——内存分配与回收策略

所谓冒险,在允许担保失败的情况下,将Survivor无法存放的存活对象,由老年代进行分配担保,这个步骤是有一定风险的。因为新生代中会有多少对象将发生晋升(新生代—>老年代)我们无法知道。

  • 如果老年代中有足够的空间来容纳新生代中所有对象(最坏的情况,所有新生代对象都晋升),那么这种情况相对来说安全的多。
  • 如果是老年代没有足够的空间来容纳新生代中所有对象,且我们无法知道有多少新生代对象发生晋升。简单来说就是我们无法确定老年代是否有足够的空间存放新生代中晋升的对象,我们只能以历史平均晋升值作为参考,那么这种情况就可能会发生担保失败。

取历史平均值来比较其实仍然是一种赌概率的解决办法,也就是说假如某次Minor GC存活后的对象突增,远远高于历史平均值的话,依然会导致担保失败。如果出现了担保失败,那就只好老老实实 地重新发起一次Full GC,这样停顿时间就很长了。虽然担保失败时绕的圈子是最大的但通常情况下都还是会将-XX:HandlePromotionFailure开关打开,避免Full GC过于频繁。
会将**-XX:HandlePromotionFailure开关打开,避免Full GC过于频繁。

注意:该参数JDK1.7以后就废弃了, 只要老年代的连续空间大于新生代对象的总大小或者历次晋升到老年代的对象的平均大小就进行MinorGC,否则Fu11GC

本文参考自《深入理解Java虚拟机》(第三版)文章来源地址https://www.toymoban.com/news/detail-439688.html

到了这里,关于深入理解Java虚拟机——内存分配与回收策略的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [AIGC] 深入理解 Java 虚拟机(JVM)的垃圾回收

    一、是什么 Java 虚拟机(JVM)的垃圾回收(Garbage Collection)是一种自动内存管理机制,用于释放不再使用的对象所占用的内存空间。垃圾回收的目标是回收那些不再被程序引用的对象,以避免内存泄漏和内存溢出等问题。 二、为什么需要垃圾回收 在 Java 程序中,对象的创建

    2024年02月21日
    浏览(48)
  • 深入探讨Java虚拟机(JVM):执行流程、内存管理和垃圾回收机制

    目录 什么是JVM? JVM 执行流程 JVM 运行时数据区 堆(线程共享) Java虚拟机栈(线程私有) 什么是线程私有? 程序计数器(线程私有) 方法区(线程共享) JDK 1.8 元空间的变化 运行时常量池 内存布局中的异常问题 1.  Java堆溢出 2.  虚拟机栈和本地方法栈溢出 JVM 类加载 1.

    2024年02月09日
    浏览(45)
  • Java虚拟机(JVM)垃圾收集器、新生代、老年代、永久代以及内存分配策略

    在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。而新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。 新生代中一般保存新出现的对象,所以每次

    2024年02月04日
    浏览(81)
  • 深入理解Java虚拟机(二)Java内存区域与内存溢出异常

            对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好。不过,也正是因为Java程序员把控制内存的权力交给了Java虚拟机,一旦出

    2024年02月16日
    浏览(71)
  • 《深入理解Java虚拟机》读书笔记:Java内存区域

    Java内存区域包含程序计数器、虚拟机栈、本地方法栈、Java堆、方法区五个区域。 运行时数据区分类   Java内存区域   程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的信号指示器。 字节码解释器工作时就是通过改变这个计数

    2024年02月14日
    浏览(50)
  • 深入理解Java虚拟机jvm-对象的内存布局

    在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例 数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机对象的对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈 希码(HashCode)、GC分代年龄、锁状态标志、

    2024年02月09日
    浏览(54)
  • 深入理解 slab cache 内存分配全链路实现

    本文源码部分基于内核 5.4 版本讨论 在经过上篇文章 《从内核源码看 slab 内存池的创建初始化流程》 的介绍之后,我们最终得到下面这幅 slab cache 的完整架构图: 本文笔者将带大家继续从内核源码的角度继续拆解 slab cache 的实现细节,接下来笔者会基于上面这幅 slab cache 完

    2024年02月02日
    浏览(47)
  • Python内存管理与垃圾回收机制:深入理解与优化【第138篇—RESTful API】

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在Python编程中,内存管理与垃圾回收机制是至关重要的主题。了解Python如何管理内存和处理垃圾回收对于编写高效、稳定的程序至关重要。本

    2024年03月18日
    浏览(52)
  • Java垃圾回收机制深入理解

    Java垃圾回收机制是Java虚拟机(JVM)的核心组件之一,对于内存管理起到至关重要的作用。它能自动追踪并管理应用程序中创建的对象,当这些对象不再使用时,垃圾回收机制会自动回收其占用的内存,使这部分内存能够被再次利用。此机制极大地减少了开发者需要手动管理

    2024年02月09日
    浏览(37)
  • 深入理解Linux虚拟内存管理

    Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理(一) 深入理解Linux虚拟内存管理(二) 深入理解Linux虚拟内存管理(三) 深入理解Linux虚拟内存管理(四) 深入理解Linux虚拟内存管理(五) 深入理解Linux虚拟内存

    2024年02月06日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包