JVM内存管理和JVM垃圾回收机制

时间:2024-01-01 11:47:27

JVM内存管理和JVM垃圾回收机制(1)

这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,另外JVM分别对新生代和旧生代采用不同的垃圾回收机制。

AD:

你对JVM内存组成结构和JVM垃圾回收机制是否熟悉,这里和大家简单分享一下,希望对你的学习有所帮助,首先来看一下JVM内存结构,它是由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示。

JVM学习笔记 JVM内存管理和JVM垃圾回收

JVM内存组成结构

JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示:

JVM内存管理和JVM垃圾回收机制新生代, 旧生代" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; list-style: none;">

1)堆

所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace和ToSpace组成,结构图如下所示:

JVM内存管理和JVM垃圾回收机制新生代, 旧生代" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; list-style: none;">

新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例旧生代。用于存放新生代中经过多次垃圾回收仍然存活的对象

2)栈

每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果

3)本地方法栈

用于支持native方法的执行,存储了每个native方法调用的状态

4)方法区

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(PermanetGeneration)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。介绍完了JVM内存组成结构,下面我们再来看一下JVM垃圾回收机制。

JVM内存管理和JVM垃圾回收机制(2)

这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,另外JVM分别对新生代和旧生代采用不同的垃圾回收机制。

AD:

JVM垃圾回收机制

JVM分别对新生代和旧生代采用不同的垃圾回收机制

新生代的GC:

新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,

用javavisualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装载,当旧生代也满了后,就会报outofmemory的异常,如下图所示:

JVM内存管理和JVM垃圾回收机制新生代, 旧生代" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; list-style: none;">

在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew)

1)串行GC

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定

2)并行回收GC

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数

3)并行GC

与旧生代的并发GC配合使用

旧生代的GC:

旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行GC(SerialMSC)、并行GC(parallelMSC)和并发GC(CMS),具体算法细节还有待进一步深入研究。

以上各种GC机制是需要组合使用的,指定方式由下表所示:

JVM内存管理和JVM垃圾回收机制新生代, 旧生代" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; list-style: none;">

文章二

在JVM启动时,就已经保留了固定的内存空间给Heap内存,这部分内存并不一定都会被JVM使用,但是可以确定的是这部分保留的内存不会被其他进程使用,这部分内存大小由-Xmx 参数指定。而另一部分内存在JVM启动时就分配给JVM,作为JVM的初始Heap内存使用,这部分内存是由 -Xms 参数指定。

在下图中,我们可以看到在Young Generation中有一个叫Eden Space的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to),它们的大小总是一样,它们用来存放每次垃圾回收后存活下来的对象。

JVM内存管理和JVM垃圾回收机制

首先我们需要知道JVM内存申请过程:

  1. JVM 会试图为相关Java对象在Eden中初始化一块内存区域
  2. 当Eden空间足够时,内存申请结束;否则到下一步
  3. JVM 试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
  4. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
  5. 当OLD区空间不够时,JVM 会在OLD区进行完全的垃圾收集(0级)
  6. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory”错误

接下来介绍GC:

以下概念截取自: http://www.daniel-journey.com/archives/97

GC:自动内存管理程序,它负责保证被引用的对象始终在内存中;同时把不被应用的对象从内存中释放。被引用的对象称之为Live 对象;不被引用的对象就是Dead对象,是需要回收的。GC会自动计算对象被引用的情况,只要对象不在被引用,相应的内存就会被回收,而C++中需要开发人员通过代码来“显示”地回收内存,如果程序员没有回收就会导致内存的泄露(内存泄露的原因有很多种这只是其中一个)。C++中还有经常出现的一个问题是一个对象在还有其他引用存在的情况下,就被程序给回收了,导致其他引用访问该对象时出现严重错误。另外,GC非常重要的一点就避免内存碎片,道理跟windows的磁盘整理一样,把使用中各个内存块整合起来,这样才能保证有足够的空间来存储大对象。总而言之,GC试图使开发者的程序更加有效有序地使用有限的内存。

Hostspot虚拟机采用了“分代回收”的策略,而“分”的非常重要的一个依据就是根据对象存在的时间的长短分成若干个“代(Gerneration)”,每个代上可以采取不同的GC策略。而采用这种“分代回收”策略是利用了2条潜规则,而且这两条潜规则不只限于Java

  • 绝大对数的对象不会被长时间引用,这些对象在他的“青年期”就会被回收。
  • 几乎不存在很老和很新对象之间的引用

依据这两条潜规则,Hotspot分离出新生代(Yound Generation)和旧生代(Old Generation)。由于新生代的空间通常都比较小而且可能存在大量不再被引用的对象,所以针对新生代的GC执行频率高、速度快。在新生代中存在了一定时间还没被GC掉的对象最终会被提升到旧生代。旧生代空间比新生代的要大,但它的占用率增长会比较缓慢,因此,旧生代的GC执行频率低,但需要更长的时间来完成。

另外还有一个永生代(Permanent Generation),永生代中的保存的对象都是JVM用来方便管理GC的,例如类和方法对象以及它们的描述对象。(Sun的JVM不回收PermGen,由于动态语言产生类会比较多,有时就会出现PermGen Overflow。另外推荐用JRockit JVM)

有了这些基础,我们就能够描述GC的执行过程:

  • 在Young Generation块中,垃圾回收一般用Copying的算法,速度快。每次GC的时候,存活下来的对象首先由Eden拷贝到某个SurvivorSpace, 当Survivor Space空间满了后,剩下的live对象就被直接拷贝到OldGeneration中去。因此,每次GC后,Eden内存块会被清空。
  • 在Old Generation块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求。
  • 垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾;1级或以上为部分垃圾回收,只会回收Young中的垃圾,内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况