(四)-对象内存的分配策略

简介: Java所承诺的自动内存管理主要是:给对象分配内存,回收分配给对象的内存.在Java虚拟机的五块内存空间中,程序计数器、Java虚拟机栈、本地方法栈内存的分配和回收都具有确定性,一般在编译阶段就能确定需要分配的内存大小,并且由于都是线程私有,因此它们的内存空间都随着线程的创建而创建,线程的结束而回收.

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. 新生代中使用"复制"算法实现垃圾回收,老年代中使用'标记-清除"或"标记-整理"算法实现垃圾回收,只有使用"复制"算法的区域才需要分配担保,因此新生代需要分配担保,而老年代不需要分配担保.
目录
相关文章
|
2月前
|
存储 算法 Java
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
|
17天前
|
算法 Java
堆内存分配策略解密
本文深入探讨了Java虚拟机中堆内存的分配策略,包括新生代(Eden区和Survivor区)与老年代的分配机制。新生代对象优先分配在Eden区,当空间不足时执行Minor GC并将存活对象移至Survivor区;老年代则用于存放长期存活或大对象,避免频繁内存拷贝。通过动态对象年龄判定优化晋升策略,并介绍Full GC触发条件。理解这些策略有助于提高程序性能和稳定性。
|
1月前
|
NoSQL 算法 Redis
redis内存淘汰策略
Redis支持8种内存淘汰策略,包括noeviction、volatile-ttl、allkeys-random、volatile-random、allkeys-lru、volatile-lru、allkeys-lfu和volatile-lfu。这些策略分别针对所有键或仅设置TTL的键,采用随机、LRU(最近最久未使用)或LFU(最少频率使用)等算法进行淘汰。
45 5
|
1月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
126 7
|
1月前
|
缓存 监控 算法
Python内存管理:掌握对象的生命周期与垃圾回收机制####
本文深入探讨了Python中的内存管理机制,特别是对象的生命周期和垃圾回收过程。通过理解引用计数、标记-清除及分代收集等核心概念,帮助开发者优化程序性能,避免内存泄漏。 ####
48 3
|
2月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
183 13
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
71 1
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
168 4
|
2月前
|
存储 分布式计算 算法
1GB内存挑战:高效处理40亿QQ号的策略
在面对如何处理40亿个QQ号仅用1GB内存的难题时,我们需要采用一些高效的数据结构和算法来优化内存使用。这个问题涉及到数据存储、查询和处理等多个方面,本文将分享一些实用的技术策略,帮助你在有限的内存资源下处理大规模数据集。
37 1
|
3月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
113 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS