堆的核心概述
- 一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域。
- java堆区在jvm启动的时候即被创建,其空间大小也就确定了。是jvm管理的最大的一块内存区域(堆内存的大小是可以调节的)。
- 《java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
- 所有的线程共享java堆,在这里还可以划分线程私有的缓冲区。
- “几乎”所有的对象实例都在这里分配内存—从实际使用角度看。
- 数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。
- 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。
- 堆,是GC执行垃圾回收的重点区域。
- 内存细分:
java7之前堆内存逻辑上分为三部分:新生区+养老区+永久区
java8之后堆内存逻辑上分为三部分:新生区+养老区+元空间
堆空间大小设置
-
java堆区用于存储java对象实例,那么堆的大小在jvm启动时就已经设定好了,可以通过选项"Xmx"和"Xms"来进行设置。其中:
"Xms"用于表示堆区的起始内存,等价于-XX:InitialHeapSize
"Xmx"用于表示堆区的最大内存,等价于-XX:MaxHeapSize -
一旦堆区中的内存大小超过"Xmx"所指定的最大内存时,将会抛出OutOfMenoryError异常,即通常所说的OOM。
-
通常会将 -Xms 和 -Xmx 两个参数配置成相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分配计算堆区的大小,从而提高性能。
-
默认情况下。初始内存大小:物理电脑内存大小 / 64;最大内存大小:物理电脑内存大小 / 4。
对象分配过程
为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配,在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否在内存空间中产生内存碎片。
- new的对象先放伊甸园区,此区有大小限制。
- 当伊甸园区的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC 或 Young GC),将伊甸园区中的不再被其他对象所引用的对象进项销毁,再加载新的对象放到伊甸园区。
- 然后将伊甸园中的剩余对象移动到幸存者0区。
- 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。
- 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。
- 啥时候去养老区呢?可以设置次数,默认是15次。
Minor GC、Major GC、Full GC
JVM在进行GC时,并非每次都对三个内存(新生代、老年代;方法区)区域一起回收的,大部分时候回收的都是指新生代。针对HotSport VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)。
- 部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:(1):新生代收集(Minor GC / Young GC):只是新生代(Eden \ S0,S1)的垃圾收集;(2):老年代收集(Major GC / Old GC):只是老年代的收集。注意,很多时候Maior GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。(3):混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。目前只有G1 GC会有这种行为。
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
堆空间的参数设置
- -XX:+PrintFlagsInitial:查看所有的参数的默认初始值。
- -XX:+PrinitFlagsFinal:查看所有的参数的最终值(可能会存在修改,不再是初始值)。
- -Xms:初始堆空间内存(默认为物理内存的1/16)。
- -Xmx:最大堆空间内存(默认为物理内存的1/4)。
- -Xmn:设置新生代的大小(初始值及最大值)。
- -XX:NewRatio:配置新生代与老年代再堆结构的占比。
- -XX:SurvivorRatio:设置新设古代中Eden和S0/S1空间的比例。
- -XX:MaxTenuringThreshold:设置新生代垃圾的最大年龄。
- -XX:+PrintGCDetails:输出详细的GC处理日志。
- -XX:HandlePromotionFailure:是否设置空间分配担保(以下做说明):
在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于,则此次Minor GC是安全的;如果小于,则虚拟机会查看-XX:HandlePromotionFailure设置值是否允许担保失败,如果HandlePromotionFailure=true,那么会继续检查老年代最大可能连续空间是否大于历次晋升到老年代的对象的平均大小:如果大于,则尝试进行一次Minor GC,但这次的Minor GC依然是有风险的;如果小于,则改为进行一次Full GC。如果HandlePromotionFailure=false,则改为进行一次Full GC。
在JDK6 Update24之后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略(可理解为默认设置为true),观察OpenJDK中的源码变化,虽然源码中还定义了HandlePromotionFailure参数,但是在代码中已经不会再使用它。JDK6 Update24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行Minor GC,否则将进行Full GC。