堆整体
java堆的特点
- Java 堆(Java Heap)是 java 虚拟机所管理的内存中最大的一块
- java 堆被所有线程共享的一块内存区域
- 虚拟机启动时创建java堆
- java 堆的唯一目的就是存放对象实例。
- java 堆是垃圾收集器管理的主要区域。
- 从内存回收的角度来看, 由于现在收集器基本都采用分代收集算法, 所以Java堆可以细分为:新生代(Young)和老年代(Old)。 新生代又被划分为三个区域 Eden、From Survivor, To Survivor 等。无论怎么划分,最终存储的都是实例对象, 进一步划分的目的是为了更好的回收内存, 或者更快的分配内存。
- java 堆的大小是可扩展的, 通过 -Xmx 和 -Xms 控制。
- 如果堆内存不够分配实例对象, 并且对也无法在扩展时, 将会抛出 outOfMemoryError异常。
堆内存划分:
- 堆大小 = 新生代 + 老年代。堆的大小可通过参数–Xms(堆的初始容量)、-Xmx(堆的最大容量) 来指定。
- 其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to 以示区分。默认以 Edem:from:to = 8:1:1 (可以通过参数 –XX:SurvivorRatio)来设定 。
- 即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
- JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
- 新生代实际可用的内存空间为 9/10 ( 即90% ) 的新生代空间。
年轻代和老年代的划分是为了更好的内存分派及回收,提高效率
堆是垃圾回收机制的重点区域,我们知道垃圾回收机制有三种,minor gc,major gc,full gc 针对于堆的就是前两种。年轻代的叫 minor gc,老年代的叫 major gc
年轻代
新生代(Young)几乎是所有 java 对象出生的地方,即 java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不会长久的存活, 具有朝生夕死的特点。
老年代
老年代中存放的对象是存活了很久的,年龄大于15的对象,在老年代触发的 gc 叫 major gc 也叫full gc,full gc 会包含年轻代的 gc,但老年代只要执行 gc 就一定是 full gc
full gc 采用的是 标记-清除算法,会产生内存碎片。在执行 full gc 的情况下,会阻塞程序的正常运行,老年代的 gc 比年轻代的 gc 效率上慢10倍以上,对效率有很大的影响
永久代
永久代是 hotspot 虚拟机,也就是我们使用的java虚拟机的特有的概念,他不属于堆内存,是方法区的一种实现,各大厂商对方法区有各自的实现。永久代存放jvm运行时,需要的类,包含java库的类和方法,在触发 full gc 的情况下,永久代也会被进行垃圾回收。永久代的内存溢出也就是 pergen space。
元空间
元空间是metaspace,在jdk1.8的时候,jvm移除了永久代的概念,元空间也是对java虚拟机的方法区的一种实现。元空间与永久代最大的区别在于,元空间不在虚拟机中,使用本地内存。通过配置如下参数可以更改元空间的大小。
-XX:MetaspaceSize:初始空间的大小。达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
永久代的回收会随着 full gc进行移动,消耗性能。每种类型的垃圾回收都需要特殊处理元数据。将元数据剥离出来,简化了垃圾收集,提高了效率。
垃圾回收
Minor GC(简称GC)
Minor GC 是发生在新生代中的垃圾收集动作, 所采用的是复制算法。
GC 一般为堆空间某个区发生了垃圾回收,
新生代(Young)几乎是所有 java 对象出生的地方。即 java 对象申请的内存以及存放都是在这个地方。java 中的大部分对象通常不会长久的存活, 具有朝生夕死的特点。
当一个对象被判定为 "死亡" 的时候, GC 就有责任来回收掉这部分对象的内存空间。
新生代是收集垃圾的频繁区域。
回收过程
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳(上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。
但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。
Full GC
Full GC 基本都是整个堆空间及持久代发生了垃圾回收,所采用的是标记-清除算法。
现实的生活中,老年代的人通常会比新生代的人 "早死"。堆内存中的老年代(Old)不同于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长,一般是 Minor GC的 10倍以上。
另外,标记-清除算法 收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作