JVM的新生代、老年代、MinorGC、MajorGC

JVM中的堆,一般分为三大部分:新生代、老年代、永久代:

image

一:新生代:主要是用来存放新生的对象。

一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。 新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。 Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。

当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。 ServivorTo:保留了一次MinorGC过程中的幸存者。 ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。 MinorGC的过程:MinorGC采用复制算法。在此我向大家推荐一个架构学习交流圈。交流学习伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);

然后,清空Eden和ServicorFrom中的对象;

最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。
二:老年代:主要存放应用程序中生命周期长的内存对象。

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

MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。在此我向大家推荐一个架构学习交流圈。交流学习伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。 当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

三:永久代 指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。
所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。在此我向大家推荐一个架构学习交流圈。交流学习伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

因此,默认情况下,元空间的大小仅受本地内存限制。
类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制. 采用元空间而不用永久代的几点原因:

1、为了解决永久代的OOM问题,元数据和class对象存在永久代中,容易出现性能问题和内存溢出。   
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出(因为堆空间有限,此消彼长)。   
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。   
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。

### JVM新生代年代的比例配置及默认值 #### 默认比例 在 Java 虚拟机 (JVM) 的堆内存分配中,默认情况下,新生代 (Young Generation) 年代 (Old Generation, 或 Tenured Generation) 的比例为 **1:2**。这意味着整个堆空间会被划分为三部分: - 新生代占据总堆的三分之一。 - 年代占据总堆的三分之二。 这种划分方式适用于大多数应用场景,但在某些特定场景下可能需要调整该比例以优化性能[^1]。 #### 配置方法 可以通过以下几种方式进行新生代年代的比例配置: 1. **使用 `-XX:NewRatio` 参数** - 此参数用于设置新生代年代之间的比例关系。例如,如果指定 `-XX:NewRatio=3`,则表示年代的空间是新生代的 3 倍,即新生代占四分之一,而年代占剩余的四分之三[^3]。 2. **使用 `-Xmn` 参数** - 可以通过直接设定新生代的具体大小来间接影响其与年代的比例。例如,`-Xmn256m` 表示将新生代固定为 256MB 大小。此时,年代的大小由总的堆大小减去新生代大小决定。 3. **综合配置示例** 下面是一个完整的配置例子: ```bash java -Xms1024m -Xmx1024m -Xmn256m -XX:NewRatio=3 MyApplication ``` 这里的含义如下: - `-Xms1024m`: 初始堆大小设为 1GB。 - `-Xmx1024m`: 最大堆大小也设为 1GB。 - `-Xmn256m`: 将新生代大小显式设置为 256MB。 - `-XX:NewRatio=3`: 如果未通过 `-Xmn` 显式定义,则按照此比例计算新生代年代大小。 #### 注意事项 当调整新生代年代的比例时需要注意以下几点: - 不同的应用程序有不同的垃圾收集模式需求。对于短生命周期的对象较多的情况,可以适当增加新生代的比例;而对于长生命周期对象较多的情况,则应减少新生代占比并增大年代份额。 - 若新生代过小可能导致频繁发生 Minor GC,从而降低应用程序吞吐量;反之,若新生代过大可能会延长 Major GC 时间[^2]。 ```java // 示例代码展示如何启动带有自定义堆选项的Java进程 public class HeapConfigExample { public static void main(String[] args) { System.out.println("Heap Configuration Example"); Runtime runtime = Runtime.getRuntime(); long maxMemory = runtime.maxMemory(); // 获取最大可用堆内存 long totalMemory = runtime.totalMemory(); // 当前已分配给JVM的堆内存 System.out.printf("Max Memory: %d MB%n", maxMemory / (1024 * 1024)); System.out.printf("Total Memory: %d MB%n", totalMemory / (1024 * 1024)); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值