JVM

Java 发表评论

GC是什么?了解原理吗?

GC是垃圾收集的意思(Garbage Collection),JVM通过垃圾回收机制自动回收内存来保证可用内存空间。
JVM的垃圾回收器在JDK1.7以前常用的CMS收集器,特点是短时间停顿,JDK1.7开始使用G1收集器,与其他收集器相比,它具有如下优点:并行与并发、分代收集、空间整合、可预测的停顿等。
GC的工作主要包含三方面内容:

  • 确定可回收内存
    • 确定可回收内存的内容就是确定不可用(已死)对象并进行标记,确认对象不可用(已死)的算法有:引用计数算法和可达性分析算法,其中引用计数算法存在无法处理对象之间循环引用的问题,所以采用的是可达性分析算法
      可达性分析算法的内容大概是:通过GC Roots对象作为搜索起点沿着引用链对所有对象进行可达性搜索,当一个对象到GC Roots对象没有任何引用链相连时,则会标记此对象不可用(已死)。
  • 确定何时回收内存
    • 对象在Eden区中分配且没有足够空间时,会触发一次Minor GC;
    • 老年代(Tenured区)没有足够空间时,会触发一次Full GC;
    • 方法区(Permanent区)没有足够空间时,会触发一次Full GC;
    • 调用System.gc() 或者 Runtime.gc()时(系统建议执行Full GC,但是不必然执行),会触发一次Full GC;
  • 执行回收内存
    • 回收时采用的是分代回收机制,对于新生代(Eden区+Survivor区)采用“复制-清理”算法,需要分配担保,对于老年代(Tenured区)采用“标记-整理”或“标记-清理”算法,无需分配担保。

详细的GC工作流程如下:

  • 对象在Eden区中分配且没有足够空间时,会触发一次Minor GC,在触发Minor GC前,会首先检查Survivor区的“From”区中是否存在达到晋升老年代(Tenured区)的年龄的可用(存活)对象。
  • 如果不存在则执行Minor GC,首先标记Eden、Survivor区中对象是否可用(存活),然后将Eden区的可用对象复制到Survivor区的“To”区。
  • 接下来将Survivor区的“From”区中未达到晋升老年代(Tenured区)的年龄的可用(存活)对象复制到Survivor区的“To”区。
  • 以上工作完成后,将Eden区和Survivor区的“From”区清空,最后将Survivor区的“From”区和“To”区位置置换(“To”区变“From”区、“From”区变“To”区),保证Survivor区的“To”区始终为空。
  • 还有如果Survivor区的“To”区被填满,会将其中所有可用(存活)对象转移到Tenured区,至此Minor GC结束。
  • 在触发Minor GC前,Survivor区的“From”区中如果存在达到晋升老年代(Tenured区)的年龄的可用(存活)对象,则会触发一次Full GC,不再执行Minor GC。
  • 另外除了以上Full GC的触发条件,然后一些其他条件会触发Full GC,例如:
    • 老年代(Tenured区)没有足够空间时
    • 方法区(Permanent区)没有足够空间时
    • 调用System.gc() 或者 Runtime.gc()时(系统建议执行Full GC,但是不必然执行)
  • Full GC中会对Tenured区、Permanent区中的对象是否可用进行标记,然后清理不可用对象,整理可用对象。

要注意的是,GC对于大对象的处理效率低下(Minor GC时会发生大量内存复制),所以在处理大对象的分配时,为了提升效率,会直接将大对象分配到Tenured区,避免GC。

分代回收策略中的Minor GC是对新生代进行回收,采用的是“复制-清理”算法,而Major GC是对老年代进行回收,采用的则是“标记-整理”或“标记-清理”算法,Major GC和Minor GC属于Full GC的一部分,而Full GC则是对整个堆进行收集,包括新生代、老年代、永久代。

”所有的 Minor GC 都会触发”全世界的暂停(stop-the-world)”,停止应用程序的线程。”,对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就是,Eden区中的大部分对象都能被认为是垃圾,永远也不会被复制到Survivor区或者Tenured区,所以一般Minor GC执行暂停的时间很短。如果正好相反,Eden区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。

JVM内存模型了解吗?

2986704-13618a69a94240b8.png
JVM1.6内存结构

Java虚拟机规范中将Java运行时数据分为六种。

  • 程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。
  • Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。
  • 本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
  • Java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存。
  • 方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
  • 运行时常量池:代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。

上一篇:
下一篇:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *