JVM学习笔记二:JVM堆

概述

堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:

  • 新生区 young
  • 老年区 old
  • 永久区 perm

下图是更加详细的分区:
在这里插入图片描述

新生区 young

新生区概述:

新生区当中包括eden区,from区和to区(from和to即两个幸存者区)
因为young区的GC为复制算法,所以有两块from和to区域,当有一次minorGC没有将伊甸区的对象回收时。此对象被放到from区。若此对象下一次minorGC仍没有被回收,他将会和从伊甸区新来的幸存对象一起放到to区,此时from和to区互换身份,如此往复,。所以说幸存者1区和2区的角色是交替的,并不固定。当15次minorGC之后对象仍没有被回收,他就将进入到old区(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),同时,如果两个幸存者区都满了,幸存的对象也会移动到养老区,若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。

  • tips:如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:
    (1)Java虚拟机的堆内存设置不够,可以通过参数**-Xms、-Xmx**来调整。
    (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

新生区 minorGC:

概述

新生区使用的GC是minorGC,他是通过复制算法实现的。
Minor GC会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区中放不下,那么剩下的活的对象就被移到Old generation中,也即一旦收集后,Eden是就变成空的了
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,通过-XX:MaxTenuringThreshold 来设定参数),这些对象就会成为老年代。

-XX:MaxTenuringThreshold —— 设置对象在新生代中存活的次数

优缺点:

  • 复制算法的优点是不会产生内存碎片。
  • 复制算法它的缺点也是相当明显的。
      1、它浪费了一半的内存,这是最突出的缺点
      2、如果对象的存活率非常高,那么复制大量引用的成本也会很高。

老年区 old

老年区概述:

新生代的对象年龄默认达到15时,进入老年代。
新生区和老年区的比例一般为一比二。新生区占三分之一,老年区占三分之二。因为绝大部分对象都是临时对象,
新生区的eden区和from区,to区的比例为:8:1:1
在这里插入图片描述

在这里插入图片描述

老年区 GC:

概述:

全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但并不是绝对的)。Major GC的速度一般要比Minor GC慢上10倍以上 。

fullGC之一:标记清除:

在这里插入图片描述
优点:
不需要额外内存空间。

缺点:
算法执行时要暂停应用并进行两次扫描(先扫描定位,后扫描删除)耗时严重,会产生内存碎片,即这种方式清理出来的空闲内存是不连续的,这点不难理解,我们的死亡对象都是随即的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。而为了应付这一点,JVM就不得不维持一个内存的空闲列表,这又是一种开销。而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

在Java语言里,可作为GC Roots对象一般为虚拟机栈,本地方法栈和方法区内的属性或常量引用的对象。没有被GC Roots引用的对象会被回收。

fullGC之二:标记整理:

标记整理也叫标记压缩:
先标记出来存活的对象,然后统一向一端移动对象。形成可回收与不可回收的边界,边界另一边的对象将被回收。
在这里插入图片描述

优点:
因为对象的存储是连续的,JVM只需要持有一个内存的起始地址即可,相对于标记清除维护一个空闲列表显然少了许多内存开销。且也不需要额外的内存空间。

缺点:
标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
效率上来说,标记/整理算法要低于复制算法。

fullGC实际上一般使用标记清除压缩的方式,将两者集合使用。
还有一种已经不再使用的引用计数法,无法解决循环引用的问题。

永久区 perm(java7之前)

永久存储区是一个常驻内存区域,在方法区中,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

永久区其实是在方法区内实现的。

JDK8开始废弃永久代(PermGen)迎来元空间(Metaspace)存储的位置也从方法区中换到了本地内存中(Metaspace)。

发布了16 篇原创文章 · 获赞 2 · 访问量 444

猜你喜欢

转载自blog.csdn.net/qq_31314141/article/details/104245686