Java对象内存分配流程

一、前言

Java对象内存分配流程是Java虚拟机(JVM)中的重要过程,它负责在堆内存和栈内存中分配对象的空间,并管理对象的生命周期。本文将详细介绍Java对象内存分配流程,帮助大家更好地理解JVM的工作原理。

二、Java对象内存分配流程

2.1、分配流程

在这里插入图片描述
在Java中,当创建一个新的对象实例时,JVM会执行new指令。这个指令会触发Java对象内存分配流程。

首先,JVM会进行逃逸分析,以确定该对象是否有可能被外部访问或共享。如果逃逸分析表明该对象只在其方法内部被引用,那么该对象可以在堆栈上分配内存。这样可以减少堆内存的分配,并且不需要进行垃圾回收。

如果逃逸分析表明该对象可能在多个线程中被共享,那么该对象将在堆内存中分配内存。这样可以避免对该对象的线程安全访问,从而提高程序的性能。

如果对象没有逃逸,且栈空间足够,则JVM会首选打散后栈上分配。打散是指将对象分配到多个栈帧中,以避免栈溢出。

如果栈上分配失败,则JVM会尝试堆中线程专属内存块分配。每个线程都有一块专属内存,避免多线程同步申请空间。

如果堆中线程专属内存块分配失败,则JVM会判断是否触发条件直接进入老年代。一般尽量避免这种情况出现。

如果未触发直接进入老年代条件,则JVM会进行新生代分配。首先尝试分配到TLAB(Thread Local Allocation Buffer),如果TLAB足够,则分配成功。

如果TLAB不够,则JVM会进行YGC(Young Generation Collection),回收新生代中的大多数对象,回收后如果还有空间,则对象继续分配到新生代中。

扫描二维码关注公众号,回复: 16555637 查看本文章

随着对象不断创建,新生代空间会逐渐减少,当达到一定条件时,进行FGC(Full GC),回收整个堆中的对象。

在垃圾回收过程中,年龄足够大的对象会被移动到老年代。老年代满了之后进行FGC,回收整个堆中的对象。

2.2、什么是逃逸分析?

逃逸分析是一种有效的减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。它通过分析对象的动态作用域,判断对象是否有可能被外部访问或共享,从而决定是否需要将对象分配到堆上。

2.2.1逃逸分析主要关注两个方面的信息:

  1. 局部性逃逸:一个对象是否只在其方法内部被引用,而不会被方法外部引用。如果一个对象只在其方法内部被引用,那么该对象可以被存储在该方法的堆栈上,而不是在堆中。这样可以减少堆内存的分配,并且不需要进行垃圾回收。
  2. 线程逃逸:一个对象是否可能在多个线程中被共享。如果一个对象只在单个线程中引用,那么该对象可以被存储在堆栈上,而不是在堆中。这样可以避免对该对象的线程安全访问,从而提高程序的性能。

2.2.2逃逸分析的好处包括:

  1. 栈上分配,可以降低垃圾收集器运行的频率。
  2. 同步消除,如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。

逃逸分析是一种静态分析技术,它通过对代码的静态分析来推断对象的作用域和生命周期。虽然逃逸分析可以提供一些有用的优化信息,但它也存在一些限制和挑战,需要谨慎使用。例如,逃逸分析可能无法处理一些动态的引用关系,或者在某些情况下可能会产生错误的优化结果。

2.2.3、为什么要分配到栈上?

  • 分配和释放速度快:在栈上分配内存比在堆上分配内存要快得多,因为栈是连续的内存空间,不需要进行内存分配和释放的操作。
  • 减少内存碎片:在堆上分配内存时,由于不同对象的生命周期可能不同,会导致内存碎片化。而栈上的对象由于生命周期相同,因此不会产生内存碎片。
  • 便于调试:栈上的对象生命周期较短,因此便于调试和管理。

2.3、什么是大对象?

大对象是指需要大量连续内存空间的对象,比如过长的字符串、数组等。在Java对象内存分配流程中,大对象会直接分配到老年代,而不会经过新生代。这是因为避免为大对象分配内存时的复制操作而降低效率。

具体来说,当JVM在堆内存中分配内存时,会根据对象的年龄和大小选择不同的区域。对于大对象,JVM会直接将其分配到老年代,而不是新生代。这是因为大对象需要连续的内存空间,而新生代中的Eden区和Survivor区都是非连续的内存空间,因此无法满足大对象的内存分配需求。

为了避免程序中出现过多的大对象,影响内存分配效率,JVM会设置一个阈值,通过-XX:PretenureSizeThreshold参数来设置。当对象大小超过该阈值时,就会触发大对象直接分配到老年代的机制。

需要注意的是,只有Serial和ParNew两个垃圾收集器会使用这个参数来控制大对象的内存分配。其他垃圾收集器会使用不同的参数来控制大对象的内存分配。

2.4、什么是TLAB?

TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。由于对象一般会分配在堆上,而堆是全局共享的,因此在同一时间,可能会有多个线程在堆上申请空间。因此,每次对象分配都必须要进行同步(虚拟机采用CAS配上失败重试的方式保证更新操作的原子性),而在竞争激烈的场合分配的效率又会进一步下降。JVM使用TLAB来避免多线程冲突,在给对象分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高了对象分配的效率。
简单说,TLAB是为了避免多线程争抢内存,在每个线程初始化的时候,就在堆空间中为线程分配一块专属的内存。自己线程的对象就往自己专属的那块内存存放就可以了。这样多个线程之间就不会去哄抢同一块内存了。这是一块每个线程私有的内存分配区域,它存在于Eden区,TLAB空间的内存非常小,仅占有整个Eden空间的1%。jdk8默认使用的就是TLAB的方式分配内存。

三、总结

Java对象内存分配流程的目的是为了管理Java对象的生命周期,避免内存泄漏和不必要的内存占用,同时提高程序性能。了解Java对象内存分配流程可以帮助我们更好地理解JVM的工作原理,从而优化程序性能和减少内存消耗。

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。

猜你喜欢

转载自blog.csdn.net/wmj20001225/article/details/132663801