Java中的新生代、老年代、永久代和各种GC

转载来自:Java中的新生代、老年代、永久代和各种GC (baidu.com)

JVM中的堆,一般分为三大部分:新生代、老年代、永久代:1 新生代主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为 Eden区、SurvivorFrom、SurvivorTo三个区。
Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
SurvivorFrom:保留了一次MinorGC过程中的幸存者。
SurvivorTo:上一次GC的幸存者,作为这一次GC的被扫描者。
当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发。因此新生代空间占用率越高,Minor GC越频繁。MinorGC的过程:采用复制算法。

首先,把Eden和SurvivorFrom区域中存活的对象复制到SurvivorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)

同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)

然后,清空Eden和SurvivorFrom中的对象;最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次GC时的SurvivorFrom区。


2 老年代老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。MajorGC采用标记—清除算法:

首先扫描一次所有老年代,标记出存活的对象

然后回收没有标记的对象。
MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
3 永久代
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。
Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。
Major GC和Full GC区别
Full GC:收集young gen、old gen、perm gen
Major GC:有时又叫old gc,只收集old gen
Minor GC触发机制:
当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。通过复制算法 ,回收垃圾。复制算法不会产生内存碎片。
复制算法————————
复制算法将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的。
    当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。
    此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空间区间的同时,垃圾对象已经被一次性全部回收。


Major GC的触发机制:Major GC又称为Full GC。当年老代空间不够用的时候,虚拟机会使用“标记—清除”或者“标记—整理”算法清理出连续的内存空间,分配对象使用。


Full GC触发机制:(1)调用System.gc时,系统建议执行Full GC,但是不必然执行(2)老年代空间不足(3)方法区空间不足(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存(5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,  当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置。

<think>好的,我需要帮助用户理解Java内存模型中的新生代老年永久元空间的区别与联系。首先,我应该根据提供的引用资料,整理出每个区域的基本概念功能。 首先,新生代老年都属于Java堆的一部分,这点在引用3中提到过。新生代又分为Eden、From SurvivorTo Survivor区,主要存放新创建的对象。当对象经过多次GC仍然存活,就会晋升到老年,如引用14所述。老年用于存放长期存活的对象大对象,当新生代空间不足时也会直接分配到这里,引用4还提到老年GC的条件。 接下来是永久元空间。根据引用2,永久是方法区的实现,在JDK8之前存在,容易导致OOM错误。而元空间是JDK8引入的,使用本地内存,避免了永久的问题,如引用2所述。元空间存放类元数据等信息,而永久在堆内存中。 然后需要比较它们的区别联系。内存位置方面,新生代老年在堆中,永久以前在堆,元空间在本地内存。存放内容方面,新生代放新对象,老年放长期存活对象,永久元空间放类元数据。垃圾回收方面,新生代使用Minor GC老年用Major GC永久元空间的回收机制不同,元空间更灵活。 最后,可能用户还会想知道为什么用元空间替永久,以及各个区域的GC触发条件。例如引用4提到老年GC可能在新生代GC后触发,或者大对象分配失败时触发。引用5给出了新生代各区的具体内存分配例子,这可以帮助用户更直观地理解大小比例。 需要确保回答结构清晰,分点说明每个区域,然后总结区别与联系。同时要正确引用资料中的信息,比如引用1提到对象晋升的条件,引用2说明元空间的优势,引用3的结构划分,引用4的GC触发条件,引用5的内存大小示例。在回答过程中自然添加引用标识,避免使用Markdown格式,保持口语化但专业。</think>### Java内存区域划分详解(新生代/老年/永久/元空间) #### 一、核心概念解析 1. **新生代** 新创建对象的初始分配区域,分为$Eden$区(占80%)两个$Survivor$区(各占10%)。当$Eden$区满时触发**Minor GC**,存活对象移动到$Survivor$区,经过多次回收(默认15次)仍存活的对象晋升到老年[^1][^3]。 ```java // 示例:通过JVM参数设置新生代大小 -XX:NewSize=256m // 初始新生代大小 -XX:MaxNewSize=1024m // 最大新生代大小 ``` 2. **老年** 存放长期存活对象大对象(如超过$Eden$区大小的数组)。当老年空间不足时触发**Major GC**(通常伴随Full GC),回收效率低于Minor GC[^4][^1]。 *触发条件示例*: - 新生代对象晋升失败 - 大对象直接分配请求(如`new byte[10MB]`) - 空间分配担保失败(`HandlePromotionFailure`策略) 3. **永久JDK≤7)** 方法区的具体实现,存储类元数据、常量池等信息。由于固定大小易导致`PermGen space`错误,最大仅能设置到82MB(32位JVM)或4GB(64位JVM)[^2]。 4. **元空间(JDK≥8)** 替永久,使用本地内存(非JVM堆),默认无上限。通过`-XX:MaxMetaspaceSize`可限制大小,有效避免内存溢出[^2]。 #### 二、关键区别对比 | 特性 | 新生代/老年 | 永久 | 元空间 | |--------------|------------------------------|---------------------|-----------------------| | **存储位置** | Java堆内存 | Java堆内存(JDK7-) | 本地内存(JDK8+) | | **存储内容** | 对象实例 | 类元数据、常量池 | 类元数据 | | **GC机制** | 分收集(Minor/Major GC) | Full GC时回收 | 独立内存管理系统 | | **溢出表现** | `OutOfMemoryError: Java heap`| `PermGen space`错误 | 本地内存耗尽才会OOM | #### 三、内存结构示意图 ``` Java内存区域 ├── 堆(Heap) │ ├── 新生代(Young Generation) │ │ ├── Eden区(新对象分配区) │ │ ├── Survivor From(GC存活对象暂存) │ │ └── Survivor To(年龄计数器递增) │ └── 老年(Old Generation) └── 非堆内存 ├── 方法区(Method Area) │ ├── 永久JDK7-,已废弃) │ └── 元空间(JDK8+,本地内存) └── 其他(PC寄存器、JVM栈等) ``` #### 四、实践建议 1. **监控工具** 使用`jstat -gc <pid>`观察各区内存变化,例如: ```bash jstat -gc 12345 1000 # 每1秒打印进程12345的GC情况 ``` 输出示例: ``` S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 2048.0 2048.0 0.0 0.0 16384.0 8192.0 40960.0 20480.0 4864.0 3648.0 512.0 384.0 5 0.123 1 0.456 0.579 ``` 2. **参数调优** - 新生代老年比例:`-XX:NewRatio=2`(老年:新生代=2:1) - Survivor区比例:`-XX:SurvivorRatio=8`(Eden:Survivor=8:1:1) - 元空间限制:`-XX:MaxMetaspaceSize=256m`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值