JVM - 堆

article/2025/10/26 15:52:13

# JVM - 堆

JDK版本:1.8

# 1、堆的核心概述

堆内存针对于JVM进程是唯一的,也就是一个进程只有一个JVM,一个进程下会存在多个线程,这些线程共享同一个堆空间,其中还可以被划分为线程私有的缓冲区(Thread Local Allocation Buffer TLAB)。堆空间是Java内存管理的核心区域。

Java堆区在JVM启动时被创建,其空间大小同时也会被确定。堆空间是JVM管理的最大一块内存空间,同时堆内存空间也是可配置的。

Java虚拟机规范中规定,对可以处于物理上连续不断的内存空间中,但是逻辑上它应该被视为连续的。所有的对象实例以及数组都应当在运行时分配在堆空间上。The heap is the run-time data area from which memory for all class instances and arrays is allocated。数组和对象可能永远都不会存储在栈上,在栈帧中保存的是地址引用,这个地址引用指向对象或者数组在堆空间中的位置。在一个方法执行结束后,堆中的对象不会马上被移除,只有在进行垃圾回收的时候才会被移除。堆是GC(Garbage Collection)执行垃圾回收的重点区域。

堆内存宏观图

# 1、JDK7堆空间内部结构

Java 7及之前堆的内存逻辑上分为三个部分:

  • 新生区(Young Generation Space):其中新生区又被细分为Eden区和Survivor区。
  • 养老区(Tenure generation space):老年代。
  • 永久区(Permanent Space)

jdk7堆内存结构

# 3、JDK8堆空间内部结构

Java 8及以后堆的内存逻辑上分为三个部分:

  • 新生区(Young Generation Space):其中新生区又被细分为Eden区和Survivor区。
  • 养老区(Tenure generation space):老年代。
  • 元空间(Meta Space)

jdk8之前与之后堆内存结构变化


# 2、设置堆内存大小与OOM

# 1、堆空间大小设置

JVM堆区用于存储Java对象实例,在JVM启动时堆空间大小就会被确定下来,可以通过-Xmx-Xms选项进行配置堆空间大小。

  • -Xms:表示堆区的起始内存,等价于-XX:InitialHeapSize
  • -Xmx:表示堆区的最大内存,等价于-XX:MaxHeapSize

一旦堆区的内存大小超过-Xmx所指定的最大内存时,将会抛出OutOfMemoryError错误。通常情况下会将-Xms-Xmx两个选项配置成相同的参数值,这样做能够在JVM进行垃圾回收清理完毕堆区后不需要重新分隔计算堆区的大小从而达到提升性能的目的。

在默认情况下:

  • -Xms:物理内存大小 / 64。
  • -Xmx:物理内存大小 / 4。

# 3、年轻代与老年代

JVM堆中存储的Java对象可以被划分为两类:

  • 生命周期较短的瞬时对象,这些对象的创建和消亡都非常迅速。
  • 生命周期非常长的对象,在某些极端的情况下能够与JVM的生命周期保持一致。

JVM堆空间被细分为年轻代(Young Gen)和老年代(Old Gen)。其中年轻代又被划分为Eden区、Survivor 0区和Surivior 1区,其中Survivor 0区和Surivior 1区由于会进行交换,在宏观上是一种相对而言的区域,所以又被称为from区和to区。

堆空间详细结构图

# 1、配置新生代与老年代空间占比

新生代与老年代的空间占比在实际开发中一般不会进行调整。

首先看一下新生代中的Eden区和Survivor 0区和Survivor 1区之间的空间占比。

Hotspot虚拟机中,Eden区和Survivor 0区和Survivor 1区之间的空间占比为:8:1:1。可以通过选项-xx:SurvivorRatio选项调整新生代中的空间占比,如:-xx:SurvivorRatio=8JVM中几乎所有的Java对象都是在Eden区被new出来的。新生代最大内存可通过-Xmn选项进行配置,一般这个参数使用默认值即可。

新生代与老年代在堆中的空间占比。

Hotspot虚拟机中,新生代与老年代的空间占比为1:2,这个比例可以通过-XX:NewRatio选项进行配置。默认-XX:NewRatio=2代表新生代的空间占比为1,老年代空间占比为2,新生代占整个堆空间的1 / 3。可以修改配置如:-XX:NewRatio=4代表新生代空间占比为1,老年代占比为4,新生代占整个堆空间的1 / 5。


# 4、对象分配过程

为新对象分配内存是一件非常严谨和复杂的任务,不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中产生内存碎片。

为新建对象分配堆内存流程图

  • 1、Javanew关键字创建的新对象先放在Eden区。此区域有大小限制。
  • 2、当Eden区的空间被填满时,应用程序又需要创建新的Java对象,JVM的垃圾回收器将堆Eden区进行垃圾回收(Minor GC),将Eden区中不再被其它对象所引用的对象进行销毁,销毁后再将新创建的Java对象放到Eden区。
  • 3、然后将Eden区中剩余对象移动至Survivor 0区。
  • 4、如果再次触发垃圾回收,此时上次幸存下来存放在Survivor 0区中的对象,如果没有进行垃圾回收,则会将其复制到Survivor 1区。
  • 5、如果再次触发垃圾回收,此时会将其重新复制到Survivor 0区,重复步骤4、5。反复经过15次之后,还存在的对象将会移动到老年区。重复步骤4、5的次数可以通过选型-Xx:MaxTenuringThreshold=N进行配置,默认是15次。
  • 6、在老年代,如果老年代中内存不足时,将触发垃圾回收(Major GC)进行养老区内存清理。若养老区经过Major GC之后,依然无法进行对象的保存,就会抛出OutOfMemory异常。

针对Survivor 0区和Survivor 1区之间的复制交换,通常都是将复制之后为空的一个移动到Survivor 1区,即Survivor 0区总是会存放对象的,这也是为什么说Survivor 0Survivor 1区是相对而言的,谁空谁是to区。

针对垃圾回收,频繁在新生代进行垃圾收集,很少在老年代进行。


# 5、Minor GCMajor GCFull GC

JVM的针对堆空间的垃圾回收大部分都是回收的新生代,针对Hotspot VM的实现,GC按照回收区范围又分为两大中类型

  • 部分收集(Partial GC)
    • 新生代收集(Minor GC / Young GC):只是新生代的垃圾收集。
    • 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。
      • 目前只有CMSGC会有单独收集老年代的行为。
      • 很多时候Major GC会和Full GC混淆使用,需要具体辨别是老年代垃圾回收还是整堆回收。
    • 混合收集(Mixed GC):对整合新生代以及部分老年代进行垃圾收集。
      • 只有G1 GC会有这种行为。
  • 整堆收集(Full GC):针对整个Java堆和方法区的垃圾收集。

# 6、分代式GC触发机制

# 1、年轻代GC(Minor GC)触发机制

当年轻代空间不足时,就会触发Minor GC,这里指的年轻代是Eden区,Survivor区空间不足不会引发GC。每次Minor GC都会清理年轻代内存。因为Java对象大多都具备朝生夕灭的特性,所以在JVMMinor GC是非常频繁的,垃圾回收速度也比较快。Minor GC会引发STW,当JVM堆堆中的新生代进行Minor GC时会暂停其它用户线程,等待垃圾回收结束用户线程才会被恢复。

# 2、老年代GC(Major GC / Full GC)触发机制

Major GCFull GC指发生在老年代的GC,当对象从老年代消失时,就发生了Major GCFull GC

Major GC经常会伴随至少一次的Minor GC,但是这种情况并非绝对。在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程。也就是说当老年代空间不足时,通常情况下会先进行一次Minor GC,如果进行Minor GC之后老年代空间还是不足则会触发Major GC对老年代进行垃圾回收。

Major GC的速度一般会比Minor GC慢10倍以上,Major GC引发的STW时间则会比Minor GCSTW时间更长。如果在Major GC之后堆空间内存还是不足则会抛出OutOfMemory异常。

# 3、整堆回收Full GC触发机制

触发整堆垃圾回收的场景有五种:

  • 显示调用System.gc()时,系统将会建议JVM执行Full GC,但是不一定会执行。
  • 老年代空间不足。
  • 方法区空间不足。
  • 通过Minor GC后进入老年代的对象平均内存大小大于老年代当前可用的内存。
  • Eden区、Survivor Space 0(From Space)区向Survivor Space 1(To Space)区复制时,对象大小大于To Space区可用内存则把该对象转存到老年代中,且当前老年代可用内存大小小于该对象大小。

注意:Full GC是开发或者调优中要尽最大努力避免的垃圾回收机制,Full GC非常慢。


# 7、堆空间分代思想

Java中不同对象的生命周期是不同的,其中70% ~ 99%的都是临时对象。Java堆空间分配为新生代和老年代,其中新生代分为伊甸园区(Eden)、幸存者0区(Survivor 0 / from)、幸存者1区(Survivor 1 / to),其中幸存者区空间大小相同,其中to区总为空。而老年代中存放新生代中经过多次GC后依然存活的Java对象。

JVM对堆空间进行分代的意义就是为了优化GC的性能,可以理解为分治思想。如果没有分代,所有Java对象都在一起,触发GC之后需要寻找出没有使用的Java对象,这样的话会进行全堆扫描,而Java对象又有很大一部分是临时对象,所以将特定的对象放在一块特定的区域进行专治,针对不同代的Java对象使用不同的垃圾回收机制进行垃圾回收处理,将会对JVM触发GC时的性能有很大的优化帮助。同时将堆空间分代之后,也便于对象堆内存的分配。

jdk7与8堆空间差异


# 8、堆内存分配策略

Java中新创建的对象,JVM会在Eden区中创建该对象,经历过一次Minor GC之后仍然存活并且能够被Survivor 0区容纳,该对象将会被移动到Survivor 0区中并将该对象的年龄设置为1。该对象每在Survivor区中熬过一次Minor GC其年龄就会增加1岁,当该对象的年龄到达一定的程度之后(Hotspot默认为15岁,不同的JVM会有不同的默认值)该对象就会被移动到老年代。

针对不同年龄段的Java对象JVM的堆内存分配策略如下:

  • 新创建的对象优先分配到Eden区。
  • 大对象直接分配到老年区(开发者应尽量避免程序中出现过多的大对象)。
  • 长期存活的对象分配到老年代。
  • 动态对象年龄判断:如果在Survivor区中相同年龄的所有对象大小之和大于Survivor区空间的一半,年龄大于等于该年龄的对象可直接分配到老年代,无需等待这些对象的年龄到达MaxTenuringThreshold选项中设置的阈值。
  • Java对象堆空间分配担保:-XX:HandlePromotionFailure

# 9、线程私有分配缓冲区 - TLAB

# 1、Thread Local Allocation Buffer

JVM内存模型的角度出发,对堆内存中的Eden区继续进行划分,JVM为每个线程都分配了一个私有缓存区域,它包含在Eden空间内。在多线程同时在堆空间中为Java对象分配堆内存时,使用TLAB来避免一系列的非线程安全问题,同时还能够提升堆内存分配的吞吐量,这种内存分配方式称之为快速分配策略

Open JDK衍生的JVM都提供了TLAB的设计:

堆内存tlab

在应用程序中,开发者可通过-XX:UseTLAB选项设置是否开启TLAB空间。默认情况下TLABEden空间的内存非常小,仅仅占整合Eden区空间的1%。开发者可以通过-XX:TLABWasteTargetPercent选项配置TLAB空间所占用Eden空间的百分比大小。

设置开启TLAB之后,在创建新的Java对象时,首先会尝试TLAB中进行堆内存分配,JVM也确实将TLAB作为分配堆内存的首先。一旦该对象在TLAB空间中分配堆内存失败,JVM会立即尝试通过使用加锁机制确保操作数据的原子性进而直接在Eden空间中为该对象分配堆内存。


# 10、JVM堆空间参数选项

部分参数说明:

参数选项说明
-XX:+PrintFlagsInitial查看JVM所有的参数选项的默认初始值
-XX:+PrintFlagsFinal查看JVM所有的参数的最终值(可能会存在修改,不再是初始值)
-Xms堆空间最小内存(默认为物理内存的1 / 64
-Xmx堆空间最大内存(默认为物理内存的1 / 4
-XX:NewRatio新生代与老年代在堆结构的占比(默认为1:2
-XX:SurvivorRatio新生代中EdenS0 / S1空间的比例(默认为8:1:1
-XX:MaxTenuringThreshold新生代垃圾对象的最大年龄
-XX:+PrintGCDetails输出详细的GC处理日志
-Xx:+PrintGC / -verbose:gc打印GC简要信息
-XX:HandlePromotionFalilure是否设置空间分配担保

触发Minor GC时,在正真执行Minor GC之前,JVM虚拟机会检查老年代中最大可用的连续空间是否大于新生代所有对象的总空间。

  • 如果此时老年代中最大可用空间大于新生代中所有对象的总空间,则此次Minor GC是安全的。
  • 如果小于新生代中所有对象总空间,JVM虚拟机首先会查看-XX:HandlePromotionFalilure选项是否配置允许空间分配失败担保。
    • 如果-XX:HandlePromotionFalilure=trueJVM会继续检查老年代最大连续可用空间是否大于历次晋升到老年代的对象的平均大小。
      • 如果大于,JVM会尝试进行一次Minor GC,但这次Minor GC仍然是有风险的。
      • 如果小于,JVM会直接进行一次Full GC
    • 如果-XX:HandlePromotionFalilure=trueJVM会直接进行一次Full GC

JDK6 Update 24之后,-XX:HandlePromotionFalilure选项不会再影响虚拟机的空间分配担保策略,虽然Open JDK中仍然还是定义了-XX:HandlePromotionFalilure选项,但是在代码层面已经完全没有使用这个参数选项了。JDK6 Update 24之后的规则变更为:只要老年代的最大连续可用空间大于新生代对象总大小或者历次晋升到老年代的对象的平均大小JVM就会进行Minor GC,否则将进行Full GC

Oracle JDK Document Addreshttps://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html


# 11、堆空间是分配对象的唯一选择吗?

《深入理解Java虚拟机》3版第45页抬头的段落尾写道:

​ 随着Java语言的发展,现在已经能看到些许迹象表明日后可能出现值类型的支持,即使只考虑现在,由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大、栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,所以说Java对象实例都分配在堆上页渐渐变得不是那么绝对了。

有一种特殊情况,如果经过逃逸分析(Escape Analysis)后发现一个对象并没有逃逸出方法的话,那么该对象就可能被优化成栈上分配,这样就无需在堆上进行内存分配,JVM也就无需针对此类临时对象进行垃圾回收,这也是最常见的堆外存储技术。

基于Open JDK深度定制的TaboBaoVM,其中创新的GCIH(Garbage Collection Invisible Heap)技术实现off-heap,将生命周期较长的Java对象从Heap中移动至Heap外,并且GC不能管理GCIH内部的Java对象,以此达到降低GC的回收频率和提升GC的回收效率的目的。


http://chatgpt.dhexx.cn/article/luW5UNQL.shtml

相关文章

Java虚拟机 - JVM是什么?

为什么要有JVM? JVM就是Java运行虚拟机,那么虚拟机又分为系统虚拟机和程序虚拟机,而JVM是属于程序虚拟机,所以不要看到是虚拟机就误认为JVM是系统虚拟机。 JVM是帮助Java程序开发者在开发过程中无需考虑无用的资源需要进行回收&a…

jvm虚拟机-什么是jvm

最近看了一段时间jdk源码&#xff0c;在看源码的过程中对数据的存储不是很清楚&#xff0c;当时就在想数据到底存在哪里去了&#xff1f;以前只是看博客什么的对jvm有一个大概的了解&#xff0c;但是没有深入研究。经过一段时间的研究<<深入理解java虚拟机后>>&…

Jvm系列-Jvm概述(一)

目录 1&#xff0c;什么是JVM&#xff1f; 2&#xff0c;JVM跨平台及原理 3&#xff0c;JVM的分类 4&#xff0c;JVM的位置 5&#xff0c;JVM的体系结构 6&#xff0c;Java代码的执行流程 7&#xff0c;JVM的架构模型 8&#xff0c;JVM的生命周期 9&#xff0c;三大商…

JVM的组成

Java Virtual Machine虚拟机是一种抽象化的计算机&#xff0c;通过在实际的计算机上仿真模拟各种计算机功能来实现&#xff0c;java虚拟机有自己完善的硬件架构&#xff0c;如处理器&#xff0c;堆栈&#xff0c;寄存器等&#xff0c;还具有相应的指令系统 一次编译&#xff…

JVM是什么

什么是JVM&#xff1f; 1、JVM是Java虚拟机&#xff0c;是用来执行Java字节码&#xff08;二进制的形式&#xff09;的虚拟机计算机。2、JVM是运行在操作系统之上的&#xff0c;与硬件没有任何关系。 Java的跨平台原理 跨平台&#xff1a;由Java编写的程序可以在不同的操作…

jdk、jre、jvm是什么?

JDK简介 JDK是Java Development Kit的缩写&#xff0c;是Java的开发工具包。 JDK : Java Development ToolKit(Java开发工具包)。JDK是整个JAVA的核心&#xff0c;包括了Java运行环境&#xff08;JRE&#xff09;&#xff0c;Java工具&#xff08;javac/java/jdb等&#xff0…

JVM运行原理详解

1.JVM简析&#xff1a; 作为一名Java使用者&#xff0c;掌握JVM的体系结构也是很有必要的。 说起Java&#xff0c;我们首先想到的是Java编程语言&#xff0c;然而事实上&#xff0c;Java是一种技术&#xff0c;它由四方面组成&#xff1a;Java编程语言、Java类文件格式、Java虚…

JAVA—JVM详解

JAVA—JVM详解 一、JVM 1、什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。引入Java语言…

什么是JVM?

说明&#xff1a;做java开发的几乎都知道jvm这个名词&#xff0c;但是由于jvm对实际的简单开发的来说关联的还是不多&#xff0c;一般工作个一两年&#xff08;当然不包括爱学习的及专门做性能优化的什么的&#xff09;&#xff0c;很少有人能很好的去学习及理解什么是jvm&…

JVM介绍(*)

一.什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组…

JVM01_概述、跨平台原理、分类、三大商业虚拟机

文章目录 ①. 什么是JVM&#xff1f;②. Java的跨平台及原理③. JVM的分类④. 三大商业虚拟机 ①. 什么是JVM&#xff1f; ①. JVM 是 java虚拟机&#xff0c;是用来执行java字节码(二进制的形式)的虚拟计算机 ②. jvm是运行在操作系统之上的&#xff0c;与硬件没有任何关系 …

什么是JVM

JVM 即 Java Virtual Machine&#xff0c;中文名为 Java虚拟机。 一般情况下 C/C 程序&#xff0c;编译成二进制文件后&#xff0c;就可以直接执行了&#xff1b;Java 需要使用 javac 编译成 .class 文件&#xff0c;还需要使用 Java 命令去主动执行它。JVM 就是识别 .class …

JVM详解——什么是JVM、JVM优点

JVM详解——什么是JVM、JVM优点 最近在学习Java&#xff0c;经常在书中看到JVM这三个字母&#xff0c;那到底什么是JVM呢&#xff1f; 并且JVM的知识在Java面试中也是非常受面试官青睐的知识点&#xff0c;那对于JVM我们到底要掌握哪些呢&#xff1f; 1、JVM概念 JVM是Jav…

一键禁用Win10自动更新,联想官方出品!!

对于Win10系统的强制更新&#xff0c;真的是是又爱又恨&#xff0c;更新是为了解决电脑系统存在的安全风险以及加入一些新功能&#xff0c;但经常性的开机或者关机都要更新配置&#xff0c;遇到大更新还需要花费很长的等待时间&#xff0c;更加可气的是还会出现更新失败的风险。…

关于禁止win10自动更新的方法:

关于禁止win10自动更新的方法&#xff1a; 1、winr键 输入 services.msc 进去Windows服务管理界面&#xff0c;并且找到Windows update双击进去 在常规中选择禁用&#xff0c;在恢复中选择无操作 2、winr 输入gpedit.msc 回车进入一下界面 在管理模板-windows组件 -window…

禁止windows更新唤醒计算机,windows10睡眠被自动更新唤醒的解决方法

一位用户反馈自己的windows10系统电脑在睡眠状态下总会被自动唤醒&#xff0c;感觉非常烦恼。后来发现每次win10系统开始自动更新操作系统的时候本来睡眠的电脑就会被自动唤醒。那么&#xff0c;我们该如何阻止其唤醒睡眠中的电脑呢&#xff1f;接下来&#xff0c;就随小编一起…

四步教你彻底关闭Win10自动更新

目录 一、禁用Windows Update服务 二、在组策略里关闭Win10自动更新相关服务 三、禁用任务计划里边的Win10自动更新 ​编辑四、在注册表中关闭Win10自动更新 一、禁用Windows Update服务 winR调出运行 >> services.msc >> Windows Update >> …

关于 Win 10 彻底禁用自动更新

Win 10 彻底禁用自动更新 在键盘上按 [ Win R ] 键 &#xff0c;输入 services.msc 点击确定 。 双击 [ Windows Update ] 。启动类型选择 [ 禁用 ] &#xff0c;单击 [ 停止 ] 。 点击 [ 恢复 ] &#xff0c;第一次失败选择 [ 无操作 ] &#xff0c;然后点击 [ 应用 ] 、[ 确…

Win10禁止更新设备驱动的几种方式

方法一&#xff1a;修改组策略 按下开始菜单并输入 gpedit.msc 搜索打开编辑组策略。 依次展开 计算机配置 → 管理模板 →Windows 组件 →Windows 更新 &#xff1b; 找到并双击打开 Windows 更新不包括驱动程序 &#xff1b; 将 未配置 或者 已禁用 改成 已启用 &#xf…

win10禁止chrome自动更新

win10禁止谷歌chrome自动更新 1.进入谷歌安装目录 C:\Users\Administrator\AppData\Local\Google 2.修改update权限 3.修改完成后提示&#xff1a; x