(四)-对象内存的分配策略1 对象优先在Eden区中分配2 大对象直接进入老年代3 生命周期较长的对象进入老年代4 对象年龄的动态判定5 "分配担保"策略详解

Java所承诺的自动内存管理主要是:给对象分配内存,回收分配给对象的内存.

在Java虚拟机的五块内存空间中,程序计数器、Java虚拟机栈、本地方法栈内存的分配和回收都具有确定性,一般在编译阶段就能确定需要分配的内存大小,并且由于都是线程私有,因此它们的内存空间都随着线程的创建而创建,线程的结束而回收.也就是这三个区域的内存分配和回收都具有确定性,垃圾回收器不需要在这里花费太大的精力.

而Java虚拟机中的方法区因为是用来存储类信息、常量、静态变量,这些数据的变动性较小,因此不是Java内存管理重点需要关注的区域.

而对于堆,所有线程共享,所有的对象都需要在堆中创建和回收.虽然每个对象的大小在类加载的时候就能确定,但对象的数量只有在程序运行期间才能确定,因此堆中内存的分配具有较大的不确定性.此外,对象的生命周期长短不一,因此需要针对不同生命周期的对象采用不同的内存回收算法,增加了内存回收的复杂性.

综上所述:Java自动内存管理最核心的功能是堆内存中对象的分配与回收.

1 对象优先在Eden区中分配

目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代.

在新生代中为了防止内存碎片,垃圾收集器一般都选用"复制"算法.因此,堆内存的新生代被进一步分为:Eden区+Survior1区+Survior2区.

每次创建对象时,首先会在Eden区中分配. 若Eden区已满,则在Survior1区中分配. 若Eden区+Survior1区剩余内存太少,导致对象无法放入该区域时,就会启用"分配担保",将当前Eden区+Survior1区中的对象转移到老年代中,然后再将新对象存入Eden区.

2 大对象直接进入老年代

所谓"大对象"就是指一个占用大量连续内存空间的对象,如很长的字符串及数组.

当发现一个大对象在Eden区+Survior1区中存不下的时候就需要分配担保机制把当前Eden区+Survior1区的所有对象都复制到老年代中去. 一个大对象能够存入Eden区+Survior1区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及到大量的复制,就会造成效率低下. 因此,对于大对象我们直接把他放到老年代中去,从而就能避免大量的复制操作. 那么,什么样的对象才是"大对象"呢?

  • -XX:PretrnureSizeThreshold参数 该参数用于设置大小超过该参数的对象被认为是"大对象",直接分配在老年代. 注意:该参数只对Serial和ParNew收集器有效.

3 生命周期较长的对象进入老年代

老年代用于存储生命周期较长的对象,那么我们如何判断一个对象的年龄呢?

新生代中的每个对象都有一个年龄计数器,当新生代发生一次MinorGC后,存活下来的被移动到Survivor空间的对象的年龄就加一,在Survivor区每熬过一次MinorGC,年龄就加一,当年龄超过一定值(默认15)时,就将该对象转移到老年代中.

  • -XXMaxTenuringThreshold参数 设置该参数后,只要超过该参数的新生代对象都会被转移到老年代中.

4 对象年龄的动态判定

在Survivor空间中,如果年龄相同的对象的内存大小总和超过了Survivor空间的一半,那么所有年龄相同的对象和超过该年龄的对象都会被转移到老年代中.无须等到MaxTenuringThreshold要求的年龄.

5 "分配担保"策略详解

在发生MinorGC前,JVM首先会检查老年代中最大可用的的连续空间是否大于新生代中所有对象的大小.若此条件

  • 成立,那么MinorGC可以确保安全进行.
  • 不成立,JVM会查看HandlePromotionFailure设置值是否允许担保失败.若允许,继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小
    • 若大于,将尝试一次MinorGC,虽然此次MinorGC是有风险的.
    • 若小于或HandlePromotionFailure设置不允许冒险,则进行一次FullGC.通过清除老年代中废弃数据来扩大老年代空闲空间,以便给新生代作担保.

注意:

  1. 分配担保是老年代为新生代作担保.
  2. 新生代中使用"复制"算法实现垃圾回收,老年代中使用'标记-清除"或"标记-整理"算法实现垃圾回收,只有使用"复制"算法的区域才需要分配担保,因此新生代需要分配担保,而老年代不需要分配担保.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏javathings

Java 是如何垃圾回收的?

Java 内存回收发生在 JVM 的堆区。 当一个对象到 GC Roots 没有任何引用链相连时,则说明这个对象为可回收的对象。当一个对象有引用时,则说明对象...

22340
来自专栏北京马哥教育

Python 源码阅读:list

源码位置 Include/listobject.h | Objects/listobject.c

11800
来自专栏Jackson0714

PHP内核之旅-6.垃圾回收机制

14320
来自专栏后端程序员的自我修养

CPython源码阅读笔记(2)

CPython 中基本的数据结构是 Object,所有的 Python 对象都可以用 PyObject * 来访问,CPython 中通过 Object 手动实...

20730
来自专栏java 成神之路

JVM垃圾回收算法

309110
来自专栏北京马哥教育

深度详解 Python yield与实现

学Python最简单的方法是什么?推荐阅读:Python开发工程师成长魔法 Python yield与实现 yield的功能类似于return,但是不同之处在于...

644120
来自专栏小勇DW3

垃圾回收机制重新整理篇

在Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没...

9320
来自专栏鸿的学习笔记

python源码阅读笔记之几个值得注意的点

9720
来自专栏C++

Windows核心编程:第2章 字符和字符串处理

13940
来自专栏JAVA高级架构

图解Java 垃圾回收机制

  Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题:给对象分配内存 以及 回收分配给对象的内存,而且这两个问题针对的内存区域就是...

16820

扫码关注云+社区

领取腾讯云代金券