虚拟机学习系列 - 5 - 内存分配与回收策略

本文深入探讨Java虚拟机(JVM)的垃圾回收机制,包括新生代与老年代的垃圾回收动作、大对象处理策略、对象年龄判断及空间分配担保等核心概念。通过具体实例分析,帮助读者理解不同场景下的垃圾回收行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

虚拟机学习系列 - 1 - 运行时数据区域
虚拟机学习系列 - 2 - 垃圾收集概述
虚拟机学习系列 - 3 - 垃圾收集算法
虚拟机学习系列 - 4 - 垃圾收集器
虚拟机学习系列 - 5 - 内存分配与回收策略

虚拟机学习系列 - 6 - JDK工具

虚拟机学习系列 - 附 - 虚拟机参数

虚拟机学习系列 - 附 - OQL(对象查询语言)


这节书中内容很少,给了一些例子实战。


首先明确下两个概念

1.minor GC:新生代垃圾回收动作

2.Full GC:老年代垃圾回收,经常伴随minor GC,速度一般比minor GC慢10倍以上


还是先总结一下书中内容,主要有以下几点

1.对象优先在Eden分配

2.大对象直接进入老年代

3.长期存活的对象将进入老年代

4.动态对象年龄判定

5.空间分配担保

(作者使用Serial/Serial Old)


1.对象优先在Eden分配

对象在新生代Eden分配,当Eden没有足够的空间将触发minor GC

先给出书中的例子

其中VM参数为:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

public class Main {
	private static final int _1MB = 1024 * 1024;
	public static void testAllocation(){
		byte[] a1,a2,a3,a4;
		a1 = new byte[2 * _1MB];
		a2 = new byte[2 * _1MB];
		a3 = new byte[2 * _1MB];
		a4 = new byte[4 * _1MB];
	}

	public static void main(String[] args) {
		Main.testAllocation();
	}
}

输出结果为

0.120: [GC 0.120: [DefNew: 6487K->147K(9216K), 0.0152786 secs] 6487K->6291K(19456K), 0.0155904 secs] [Times: user=0.00 sys=0.02, real=0.02 secs]
Heap
def new generation total 9216K, used 4571K [0x32730000, 0x33130000, 0x33130000)
eden space 8192K, 54% used[0x32730000, 0x32b81fa8, 0x32f30000)
from space 1024K, 14% used[0x33030000, 0x33054e88, 0x33130000)
to space 1024K, 0% used[0x32f30000, 0x32f30000, 0x33030000)
tenured generation total 10240K, used 6144K [0x33130000, 0x33b30000, 0x33b30000)
the space 10240K, 60% used[0x33130000, 0x33730030, 0x33730200, 0x33b30000)
compacting perm gen total 12288K, used 372K [0x33b30000, 0x34730000, 0x37b30000)
the space 12288K, 3% used [0x33b30000, 0x33b8d140, 0x33b8d200, 0x34730000)
ro space 10240K, 51% used [0x37b30000, 0x3805da30, 0x3805dc00, 0x38530000)
rw space 12288K, 55% used [0x38530000, 0x38bc9b50, 0x38bc9c00, 0x39130000)


下面先简单解释一下log里面写的都是什么

0.120: [GC 0.120: [DefNew:6487K(新生代回收前)->147K(新生代回收后)(9216K)新生代可用空间(Eden + Survivor), 0.0152786 secs] 6487K(Heap总量回收前)->6291KHeap总量回收后)(19456K)Heap可用空间(Eden + Survivor + 老年代), 0.0155904 secs] [Times: user=0.00 sys=0.02, real=0.02 secs]

发现新生代虽然内存变化很大,但是Heap却没有什么太大变化

程序在给a1,a2,a3分配完空间后,Eden只剩下2mb,不够给a4,所以触发一次minor GC


结果是a1,a2,a3全部进入老年代,因为Survivor无法装下他们任何一个(*)

tenured generation中the space 10240K, 60% used

然后a4便可以进入Eden了,Survivor空闲



下面我修改了一下例子

	public static void testAllocation(){
		byte[] a1,a2,a3,a4;
		a1 = new byte[4 * _1MB];
		a2 = new byte[(int)(0.8 * _1MB)];
		a3 = new byte[(int)(0.8 * _1MB)];
		a4 = new byte[(int)(2.5 * _1MB)];
	}

这是GC log为

0.100: [GC 0.101: [DefNew: 6078K->966K(9216K), 0.0114601 secs] 6078K->5882K(19456K), 0.0115323 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
Heap
def new generation total 9216K, used 3854K [0x32730000, 0x33130000, 0x33130000)
eden space 8192K, 35% used[0x32730000, 0x32a01fa8, 0x32f30000)
from space 1024K, 94% used[0x33030000, 0x33121b60, 0x33130000)
to space 1024K, 0% used[0x32f30000, 0x32f30000, 0x33030000)
tenured generation total 10240K, used 4915K [0x33130000, 0x33b30000, 0x33b30000)
the space 10240K, 48% used[0x33130000, 0x335fcce8, 0x335fce00, 0x33b30000)
compacting perm gen total 12288K, used 372K [0x33b30000, 0x34730000, 0x37b30000)
the space 12288K, 3% used [0x33b30000, 0x33b8d148, 0x33b8d200, 0x34730000)
ro space 10240K, 51% used [0x37b30000, 0x3805da30, 0x3805dc00, 0x38530000)
rw space 12288K, 55% used [0x38530000, 0x38bc9b50, 0x38bc9c00, 0x39130000)


分配a4之前,Eden里面有a1,a2,a3大概5.6mb,Eden剩余空间肯定不够a4的了

虚拟机将其中一个0.8mb的对象放在了from中,这时候Eden中大概有4.8mb被占用,也就是说,a4应该是可以存放到Eden中的,并不需要其他的操作

而虚拟机还是继续将剩下的对象扔到了老年代,然后将a4放到了Eden区中



2.大对象直接进入老年代

多大的对象才算是大对象,这个是可以控制的,虚拟机参数为-XX:PertenureSizeThreshold

注意这个参数不能像-Xmn10M这么写,需要精确到B,-XX:PertenureSizeThreshold=3145728

出现大对象容易导致内存还有不少空间时就触发GC来寻求连续的空间来安置他们

直接让大对象进入老年代可以避免Eden和Survivor以及老年代之间的大量复制(上面的例子就是新生代复制到老年代)



3.长期存活的对象将进入老年代

虚拟机给每个对象一个年龄计数器,看来对象也有年龄,没有年龄又怎么有老少之分呢

对象从出生进入Eden并经历第一次minor GC后仍然存活,并且能被Survivor容纳,将被移动到Survivor中,其年龄+1

什么样算老年了呢,默认为15岁,可以通过-XX:MaxTenuringThreshold来设置



4.动态对象年龄判定

并不是一定要达到MaxTenuringThreshold对象才会进入老年代

如果在Survivor空间中相同年龄所有对象大小的总和>Survivor空间的一半,年龄>=该年龄的对象就可以直接进入老年代



5.空间分配担保

minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代剩余空间大小,如果大于,则进行Full GC,否则查看HandlePromotionFailure设置,是否允许担保失败。

如果允许,则Minor GC,否则Full GC


当新生代在进行minor GC时候有大量对象存活,Survivor不能存放的下,他们则直接进入老年代

有多少对象存活,在实际完成工作之前是不知道的,所以就取之前每次晋升到老年代的平均大小,如果担保失败,则发起Full GC


书中例子

public class Main {
	private static final int _1MB = 1024 * 1024;
	public static void testHandlePromotion(){
		byte[] a1,a2,a3,a4,a5,a6,a7;
		a1 = new byte[2 * _1MB];
		a2 = new byte[2 * _1MB];
		a3 = new byte[2 * _1MB];
		a1 = null;
		a4 = new byte[2 * _1MB];
		a5 = new byte[2 * _1MB];
		a6 = new byte[2 * _1MB];
		a4 = null;
		a5 = null;
		a6 = null;
		a7 = new byte[2 * _1MB];
	}

	public static void main(String[] args) {
		Main.testHandlePromotion();
	}
}

log

0.144: [GC 0.144: [DefNew: 6487K->147K(9216K), 0.0069090 secs] 6487K->4243K(19456K), 0.0069719 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
0.159: [GC 0.159: [DefNew: 6541K->147K(9216K), 0.0008708 secs] 10637K->4243K(19456K), 0.0009300 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 2359K [0x32730000, 0x33130000, 0x33130000)
eden space 8192K, 27% used [0x32730000, 0x32958fe0, 0x32f30000)
from space 1024K, 14% used [0x32f30000, 0x32f54e40, 0x33030000)
to space 1024K, 0% used [0x33030000, 0x33030000, 0x33130000)
tenured generation total 10240K, used 4096K [0x33130000, 0x33b30000, 0x33b30000)
the space 10240K, 40% used [0x33130000, 0x33530020, 0x33530200, 0x33b30000)
compacting perm gen total 12288K, used 372K [0x33b30000, 0x34730000, 0x37b30000)
the space 12288K, 3% used [0x33b30000, 0x33b8d1e0, 0x33b8d200, 0x34730000)
ro space 10240K, 51% used [0x37b30000, 0x3805da30, 0x3805dc00, 0x38530000)
rw space 12288K, 55% used [0x38530000, 0x38bc9b50, 0x38bc9c00, 0x39130000)
Warning: The flag -HandlePromotionFailure=true has been EOL'd as of 6.0_24 and will be ignored


可是我无论怎么设置HandlePromotionFailure,它总是被ignored


这节内容似乎不多

之后我打算整理下虚拟机参数,书中也给了附录

还有就是简单学习下ODL,也是主要以书上附录内容为主

其余的附录对我暂时没什么意义,忽略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值