目录
垃圾收集 (Garbage Collection) 机制是 Java 的一大优势特性, 为充分榨取 JVM 性能, 避免系统因垃圾收集不及时导致的 OOM (OutOfMemory, 内存溢出)问题, 或内存饱和出现无法响应用户请求的情况, 就需要根据服务器配置及应用复杂度对 GC 策略进行优化, 以确保系统正常运行.
1 JVM 中 Java 对象的分类
JVM根据运行于其中的对象的生存时间, 将它们分为3种, 并分别存放在JVM的不同内存区域中. 这种对象存放空间的管理方式叫做 Generation管理方式.
- Young Generation (新生代, 又称年轻代): 用于存放"早逝"对象(即瞬时对象), 一般的 Java 应用中, 80%的对象都是"朝生息灭"的, 比如在创建对象或调用方法时使用的临时对象或局部变量.
- Tenured Generation (老年代): 用于存放"驻留"对象(即被引用较长时间的对象). 往往体现为一个大型程序中的全局对象或长时间被使用的对象.
- Perm Generation (永久代): 用于存放"永久"对象. 这些对象管理着运行于 JVM 中的类和方法.
2 JVM 的 GC 类型及触发条件
2.1 Young GC
又叫 Minor GC(次收集), Young GC 经常发生, 且其每次消耗的时间较短 —— 它只对Young Generation 中的对象进行垃圾收集.
-
触发条件:
在 Young Generation(新生代)的 Edne 区的空间不足以容纳新生成的对象时执行, 同时会将 Eden 区与 From Survivor 区中尚且存活的对象移动至空闲的 To Survivor 区中.
—— 程序运行过程中, 始终有一个 Survivor 区是完全处于空闲状态的, 如果不是, 说明应用程序出现故障了.
2.2 Full GC
又叫 Major GC(主收集), 是对整个 Java Heap 中的对象(不包括永久代/元空间)进行垃圾收集, 此 GC 操作耗时久, 对系统的性能影响较大, 因此在 JVM 的调优中, 很多工作是针对 Full GC 的调优 —— 要尽可能减少 Full GC 的频率.
Full GC 是一种"昂贵"的垃圾收集方式, 它要对整个Heap 进行垃圾收集, 并做一定的空间整理, 这会使 Stop-The-World 的时间变长.
- Full GC 的触发条件:
1) 年老代(Tenured)空间不足:
- 通过 Minor GC 后进入老年代的对象的体积大于老年代的可用空间;
- 由Eden块、From Space 块向 To Space 复制存活对象时, 它们的体积大于 To Space 的大小, 系统就会把这些对象转存到老年代, 而老年代的可用空间小于这些对象的体积.
2) System.gc() 被显式调用, 系统建议执行 Full GC, 但并不会立即执行 —— 非常影响程序性能, 建议禁止使用;
3) 上一次 GC 之后 Heap 各个区域空间的动态变化.
3 Java 对象生成时的内存申请过程
1) JVM 会试图为相关 Java 对象在年轻代的 Eden 区中初始化一块内存区域;
2) 当 Eden 区空间足够时, 内存申请结束. 否则执行下一步;
3) JVM 试图释放在 Eden 区中所有不活跃的对象(即 出发Young GC), 释放后若Eden空间仍然不足以放入新对象时, JVM 会试图将部分 Eden 区中活跃的对象迁移至 Survivor 区;
4) Survivor 区被用来作为 Eden 区及老年代的中间交换区域, 当老年代空间足够时, Survivor 区中存活了一定次数的对象会被迁移到老年代;
5) 当年老代空间不够时, JVM会在老年代进行完全的垃圾回收(Full GC);
6) Full GC 后, 若 Survivor 区及老年代仍然无法存放从 Eden 区复制过来的对象, 则会导致 JVM 无法在 Eden 区为新生成的对象申请内存, 即出现 "Out of Memory".
