深入理解JVM(四)——对象内存的分配策略

Java所承诺的自动内存管理主要是针对对象内存的回收和对象内存的分配。

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

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

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

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

对象优先在Eden区中分配

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

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

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

大对象直接进入老年代

所谓“大对象”就是指一个占用大量连续存储空间的对象,如数组。

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

通过-XX:PretrnureSizeThreshold参数设置大对象

该参数用于设置大小超过该参数的对象被认为是“大对象”,直接进入老年代。 注意:该参数只对Serial和ParNew收集器有效。

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

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

新生代中的每个对象都有一个年龄计数器,当新生代发生一次MinorGC后,存活下来的对象的年龄就加一,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中去。

使用-XXMaxTenuringThreshold设置新生代的最大年龄

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

相同年龄的对象内存超过Survior内存一半的对象进入老年代

如果当前新生代的Survior中,年龄相同的对象的内存空间总和超过了Survior内存空间的一半,那么所有年龄相同的对象和超过该年龄的对象都被转移到老年代中去。无需等到对象的年龄超过MaxTenuringThreshold才被转移到老年代中去。

“分配担保”策略详解

当垃圾收集器准备要在新生代发起一次MinorGC时,首先会检查“老年代中最大的连续空闲区域的大小 是否大于 新生代中所有对象的大小?”,也就是老年代中目前能够将新生代中所有对象全部装下?

若老年代能够装下新生代中所有的对象,那么此时进行MinorGC没有任何风险,然后就进行MinorGC。

若老年代无法装下新生代中所有的对象,那么此时进行MinorGC是有风险的,垃圾收集器会进行一次预测:根据以往MinorGC过后存活对象的平均数来预测这次MinorGC后存活对象的平均数。

如果以往存活对象的平均数小于当前老年代最大的连续空闲空间,那么就进行MinorGC,虽然此次MinorGC是有风险的。

如果以往存活对象的平均数大于当前老年代最大的连续空闲空间,那么就对老年代进行一次Full GC,通过清除老年代中废弃数据来扩大老年代空闲空间,以便给新生代作担保。

这个过程就是分配担保。

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

JVM性能优化入门指南

Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者Class的锁。每一个对象都有,也仅有一个 monitor。下面这个图,...

493
来自专栏向治洪

java垃圾回收机制,以及常用的回收算法

记得之前去平安面试的时候,面试官问到了垃圾回收,我当时也就是说说了垃圾回收的原理,但是具体有哪些实现策略,我当时是懵的。 概念: Java的垃圾回收机制是Jav...

1817
来自专栏JAVA高级架构

图解Java 垃圾回收机制

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

542
来自专栏python3

python3--对象之间的交互,类命名空间与对象、实例的命令空间

现在有个游戏人与狗,人定义一个类,狗定义一个类,如何让两个类之间互相交互起来,让这个游戏变得更加有意思,代码如下

901
来自专栏大内老A

在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?

在《实现存储过程的自动映射》中,我通过基于T4的代码生成实现了CUD存储过程的自动映射。由于映射的都是基于数据表结构的标准的存储过程,所以它们适合概念模型和存储...

1809
来自专栏积累沉淀

Java 内存区域和GC机制

Java垃圾回收概况   Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发...

19310
来自专栏跟着阿笨一起玩NET

C# - DynamicObject with Dynamic

本文转载:http://joe-bq-wang.iteye.com/blog/1872756

401
来自专栏张善友的专栏

SQL SERVER 2008 Hierarchyid数据类型

以往我们在关系数据库中建立树状结构的时候,通常使用ID+ParentID来实现两条纪录间的父子关系。但这种方式只能标示其相对位置。解决这类问题在SqlServe...

19010
来自专栏刘晓杰

java虚拟机和内存模型

3417
来自专栏微信公众号:Java团长

深入理解Java垃圾回收机制

Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于...

371

扫码关注云+社区