Java内存

作者: SimpleFunc | 来源:发表于2019-06-30 14:41 被阅读0次

2015-01-29 12:00

Java堆内存模型:

jvm_memory.jpg

Young Generation / Eden Space

伊甸园区,所有新对象都在该区域创建,如果Eden区域没有足够的空间容纳新对象,进行GC。内存占用到达该区域设定的任意阈值,新生代垃圾回收机制(Minor GC)就会启动。Minor GC非常频繁,速度也比较快。首先清除所有的非引用对象,并将引用对象从'From'和‘Eden’区移至'To'幸存者区。垃圾回收一旦结束,'From'与'To'的角色就会互换。

Young Generation / From

幸存者区

Young Generation / TO

幸存者区,垃圾回收过程中的所有引用对象都会从 'from' 与 'eden' 区移至此处

当Eden区内存区分配完之后,如果年老代有足够的连续的空间用来存放所有新生代区域的对象,触发Minor GC。如果对象太大,Survivor区域容纳不下,则对象直接晋升到年老代。否则使用复制算法,直接将对象复制到Survivor区域。若年老代没有足够的连续空间存储新的对象,先进行一次Minor GC,若仍不满足上述条件,则进行Full GC.若Full GC后依然内存不足,则抛出OOM异常。Full GC:清理整个内存堆,既包括年轻代也包括年老代。

Old Generation / Tenured

年老代区,根据阈值设定的不同,对象会从'To'幸运者区移至年老代区,此处进行的垃圾回收是年老代垃圾回收(Major GC)。当堆空间已满或者年老代区占满时,就会触发 Major GC。此时,通常会由一个“停止一切(Stop-the-World)”事件或线程执行垃圾回收。

Permanent Generation / Permgen space

永久代区,用于存储常量池(内存池),字段,方法数据和代码

对象分配规则

1.对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC

2.大对象直接进入老年代(大对象是指需要大量连续内存空间的对象),这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)

3.长期存活的对象进入老年代,虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,直到达到阀值对象进入老年区。

4.动态判断对象的年龄,如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

5.空间分配担保,每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。

触发Full GC的条件

1.调用System.gc();

2.年老代空间不足,新生代对象转入及创建为大对象、大数组时才会出现,当执行Full GC后空间仍然不足,则抛出java.lang.OutOfMemoryError: Java heap space错误

3.Permanet Generation空间满,Permanet Generation中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出java.lang.OutOfMemoryError: PermGen space异常。

4.CMS GC时出现promotion failed和concurrent mode failure,promotion failed是在进行Minor GC时,survivor space放不下,对象只能放入年老代,而此时年老代也放不下。concurrent mode failure是在执行CMS GC的过程中同时有对象要放入年老代,而此时年老代空间不足造成的。

5.统计得到的Minor GC晋升到年老代的平均大小大于年老代的剩余空间,例如程序第一次触发Minor GC后,有4MB的对象晋升到年老代代,那么当下一次Minor GC发生时,首先检查年老代代的剩余空间是否大于4MB,如果小于4MB,则执行Full GC。

垃圾回收算法

串行 GC(Serial GC)

针对年轻代和年老代的垃圾回收,算法使用“标记-清扫-压缩”,循环清理年轻代和年老代内存,适合内存占用较低,cpu占用较少的客户端系统

并行 GC(Parallel GC)

针对年轻代与年老代的垃圾回收,该算法使用 N 个线程(N 的值可以通过 * -XX:ParallelGCThreads=N* 设定,N 同时代表垃圾回收占用的 CPU 内核数),
轻代垃圾回收会使用 N 个线程,而年老代只用一个线程。

并行 Old GC

针对年轻代与年老代的垃圾回收,该算法对年轻代与年老代均使用 N 个线程,其他方面与并行 GC 完全一致。

并发 Mark and Sweep GC

针对年老代的垃圾回收,CMS GC 会最小化垃圾回收所需的停顿时间。该算法最适于创建高响应度的应用,且只作用于年老代。会创建多条垃圾回收的线程,与应用线程同时工作。

G1 GC

针对年轻代与年老代的垃圾回收,并行、并发、不断压缩的低停顿垃圾回收器。G1 是在 Java 7 中引入以取代 CMS GC 的。它会先将堆内存分为多个大小相等的区块,继而执行垃圾回收。通常,从活动数据最少的区块开始,因此以垃圾为先。

常见的内存溢出异常

Exception in thread "main": java.lang.OutOfMemoryError: Java heap space

这并不一定意味着内存泄露,也可能是分配的堆内存空间太小。在运行时间较长的应用中,也可能是因为一个无意识的引用被指向堆对象(内存泄露)。

Exception in thread "main": java.lang.OutOfMemoryError: PermGen space

如果加载了很多类与方法,或者创建了很多字符串常量,特别是使用 intern() 方法进行创建(从 JDK 7 开始,interned 字符串就不再存储在 PermGen 中),这类错误就会出现

Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit

当请求的数组大小超过可用的堆空间时,这类报错就会出现

***Exception in thread "main": java.lang.OutOfMemoryError: request 's' bytes for 'r' ***

交换空间溢出,当操作系统没有足够的交换空间,或另一个进程占用了系统中的所有可用内存,就会导致内存泄露。由于空间用尽,堆内存无法提供所请求的空间大小。该信息中的 's' 代表失败的请求所需的内存大小(以字节为单位),而 'r' 代表内存请求的原因

Exception in thread "main": java.lang.OutOfMemoryError: (Native method)

一个本地方法遇到内存分配失败,问题的根源在于 Java Native Interface(Java 本地接口) 中存在的错误,而非 JVM 中运行的代码错误。若是本地代码不检查内存分配错误,应由会直接崩溃,而不会出现内存溢出。

相关文章

网友评论

    本文标题:Java内存

    本文链接:https://www.haomeiwen.com/subject/bpkbcctx.html