Java虚拟机-内存分配策略

简介: Java虚拟机-内存分配策略

 image.gif编辑

 

Java技术体系中的自动内存管理可以归结为自动化的解决了两个问题:给对象分配内存和回收分配给对象的内存。前面讲的是内存回收,这章讲如何为对象分配内存。

一、内存分配策略概述

对象的内存分分配主要是指堆上分配(也可栈上分配),对象主要分配在新生代Eden区,如果启动了本地线程分配缓冲,则按照线程优先在TLAB上分配。少数情况下也会直接分配在老年代,分配的规则不固定,取决于垃圾回收器组合以及JVM中与内存相关参数的设置。

我们以Serial/Serial Old收集器为例进行介绍。

二、内存分配策略

1.对象优先在Eden区分配

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

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

大对象是指需要大量连续内存空间的Java对象,典型大对象有长字符串和数组,大对象对虚拟机的内存分配来说是一个坏消息,写程序时还要避免创建一些朝生夕死的短命大对象,经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来安置他们。

虚拟机提供-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配,这样避免再Eden及两个Survivor区间发生大量的内存复制,这个参数只对Serial和ParNew两款收集器有效,如果遇到必须使用此参数的场合,可以考虑ParNew加CMS的组合。

大对象直接在老年代进行分配的目的是避免在Eden区和两个Survivor区之间发生大量的内存复制。

3.根据对象年龄判定进入老年代

虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中,可以通过-XX:MaxTenuringThreshold参数来设置年龄阈值。

4.动态对象年龄判断

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

5.空间分配担保

关于分配担保机制JDK6前后有差别的。  

JDK6之前

当发生Minor GC时,JVM会首先检查老年代最大的可用连续空间是否大于新生代所有对象的总和,如果大于,那么这次Minor GC是安全的,如果不大于的话,JVM就需要判断HandlePromotionFailure是否允许空间分配担保。

如果允许担保失败,那么JVM会继续检查老年代最大的可用连续空间是否大于历次晋升到老年代的对象的平均大小:

    • 如果大于,则正常进行一次YGC,尽管有风险(因为判断的是平均大小,有可能这次的晋升对象比平均值大很多);
    • 如果小于,或者HandlePromotionFailure设置不允许空间分配担保,这时要进行一次FGC。

    对以上的步骤归纳一下,先看老年代的可用空间能否容下新生代的所有对象,不能的话看是否开启了分配担保机制,允许就先执行Minor GC,否则直接进行Full GC。大部分情况下还是会将HandlePromotionFailure开启分配担保,避免频繁Full GC。

    JDK6后

    HandlePromotionFailure不再影响到虚拟机的空间分配担保策略,变为只要老年代的连续空间大于新生代对象总大小或历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

    三、概念充电

    Minor GC和Major GC/Full GC:

      • 新生代GC(Minor GC:缩写YGC,指发生在新生代的垃圾回收动作,因为Java对象大多都具备朝生夕灭的特性,所以Monir GC非常频繁,一般回收速度也比较快。
      • 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但并非绝对的,在Parallel Scavenge)收集器的收集策略里就有直接进行Major GC的策略选择过程。Major GC的速度一般比Major GC慢10倍以上。

      参考资料:

        1. JVM系列(四)内存分配策略_西海幼鸟的博客-CSDN博客
        2. JVM学习之—垃圾回收与内存分配策略 - 简书
        3. JVM理论:(二/1)内存分配策略 - 湮天霸神666 - 博客园
        相关文章
        |
        7天前
        |
        存储 Java 编译器
        Java内存区域详解
        Java内存区域详解
        19 0
        Java内存区域详解
        |
        17天前
        |
        缓存 算法 Java
        Java内存管理与调优:释放应用潜能的关键
        【4月更文挑战第2天】Java内存管理关乎性能与稳定性。理解JVM内存结构,如堆和栈,是优化基础。内存泄漏是常见问题,需谨慎管理对象生命周期,并使用工具如VisualVM检测。有效字符串处理、选择合适数据结构和算法能提升效率。垃圾回收自动回收内存,但策略调整影响性能,如选择不同类型的垃圾回收器。其他优化包括调整堆大小、使用对象池和缓存。掌握这些技巧,开发者能优化应用,提升系统性能。
        |
        12天前
        |
        设计模式 监控 Java
        设计模式 - 观察者模式(Observer):Java中的战术与策略
        【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
        |
        13天前
        |
        缓存 安全 Java
        Java并发编程进阶:深入理解Java内存模型
        【4月更文挑战第6天】Java内存模型(JMM)是多线程编程的关键,定义了线程间共享变量读写的规则,确保数据一致性和可见性。主要包括原子性、可见性和有序性三大特性。Happens-Before原则规定操作顺序,内存屏障和锁则保障这些原则的实施。理解JMM和相关机制对于编写线程安全、高性能的Java并发程序至关重要。
        |
        21天前
        |
        缓存 Java C#
        【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
        【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
        60 0
        |
        2天前
        |
        Oracle Java 关系型数据库
        Java 开发者必备:JDK 版本详解与选择策略(含安装与验证)
        Oracle Java SE 支持路线图显示,JDK 8(LTS)支持至2030年,非LTS版本如9-11每6个月发布且支持有限。JDK 11(LTS)支持至2032年,而JDK 17及以上版本现在提供免费商用许可。LTS版本提供长达8年的支持,每2年发布一次。Oracle JDK与OpenJDK有多个社区和公司构建版本,如Adoptium、Amazon Corretto和Azul Zulu,它们在许可证、商业支持和更新方面有所不同。个人选择JDK时,可考虑稳定性、LTS、第三方兼容性和提供商支持。
        6 0
        |
        3天前
        |
        存储 缓存 监控
        Java内存管理:垃圾回收与内存泄漏
        【4月更文挑战第16天】本文探讨了Java的内存管理机制,重点在于垃圾回收和内存泄漏。垃圾回收通过标记-清除过程回收无用对象,Java提供了多种GC类型,如Serial、Parallel、CMS和G1。内存泄漏导致内存无法释放,常见原因包括静态集合、监听器、内部类、未关闭资源和缓存。内存泄漏影响性能,可能导致应用崩溃。避免内存泄漏的策略包括代码审查、使用分析工具、合理设计和及时释放资源。理解这些原理对开发高性能Java应用至关重要。
        |
        3天前
        |
        缓存 NoSQL Java
        使用Redis进行Java缓存策略设计
        【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
        |
        5天前
        |
        Java 程序员 编译器
        Java中的线程同步与锁优化策略
        【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。
        |
        6天前
        |
        Java 编译器
        Java并发编程中的锁优化策略
        【4月更文挑战第13天】 在Java并发编程中,锁是一种常见的同步机制,用于保证多个线程之间的数据一致性。然而,不当的锁使用可能导致性能下降,甚至死锁。本文将探讨Java并发编程中的锁优化策略,包括锁粗化、锁消除、锁降级等方法,以提高程序的执行效率。
        12 4