对象的内存分配,就是在堆上分配(也可能经过 JIT 编译后被拆散为标量类型并间接在栈上分配),对象主要分配在新生代的 Eden 区上,少数情况下可能直接分配在老年代,分配规则不固定,取决于当前使用的垃圾收集器组合以及相关的参数配置
大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。
Minor GC vs Major GC/Full GC:
在 JVM 规范中,Major GC 和 Full GC 都没有一个正式的定义,所以有人也简单地认为 Major GC 清理老年代,而 Full GC 清理整个内存堆。
进入老年代的情况:
大对象:所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是很长的字符串及数组,比遇到一个大对象更加坏的消息就是遇到一群“朝生昔死”的短命大对象,经常出现大对象容易导致内存还有不少空间时就提前触发垃圾回收以获取足够的连续空间来安置他们
-XX:PretenureSizeThreshold=3m 新生代中大于等于3m的对象,就是大对象
-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m
这里,我们给新生代10m,堆20m。
也就是说,老年代的空间=20m-10m=10m
-XX:SurvivorRatio=8
eden:s0:s1等于8:1:1
所以eden区为8m,s0为1m,s1为1m
使用的垃圾收集器为ParNew和CMS
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
打印GC的详细信息
-XX:+PrintGCDetails
打印GC的时间戳
-XX:+PrintGCTimeStamps
那这些GC日志输出在哪里,当然是文件啦。
-Xloggc:bigobject.log
jvm参数:-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m -XX:MaxHeapSize=20m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3m -XX:MaxTenuringThreshold=15 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:bigobject.log
public class mode_1 {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
byte[] array1 = new byte[2*_1MB];
array1 = new byte[2*_1MB];
array1 = new byte[2*_1MB];
byte[] array2 = new byte[128*1024];
array2 = null;
byte[] array3 = new byte[2*_1MB];//这里触发第一次minor gc
}
}
jvm参数
-verbose:gc
-XX:NewSize=10m
-XX:MaxNewSize=10m
-XX:InitialHeapSize=20m
-XX:MaxHeapSize=20m
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=10m
-XX:MaxTenuringThreshold=15
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:survivor_live.log
gc、heap 日志
OpenJDK 64-Bit Server VM (25.345-b01) for bsd-aarch64 JRE (Zulu 8.64.0.19-CA-macos-aarch64) (1.8.0_345-b01), built on Aug 2 2022 02:17:56 by "zulu_re" with gcc Apple LLVM 12.0.0 (clang-1200.0.32.28)
Memory: 16k page, physical 16777216k(428352k free)
/proc/meminfo:
0.055: [GC (Allocation Failure) 0.055: [ParNew: 8144K->488K(9216K), 0.0006237 secs] 8144K->2538K(23552K), 0.0007117 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 2989K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 30% used [0x00000007be800000, 0x00000007bea712c0, 0x00000007bf000000)
from space 1024K, 47% used [0x00000007bf100000, 0x00000007bf17a318, 0x00000007bf200000)
to space 1024K, 0% used [0x00000007bf000000, 0x00000007bf000000, 0x00000007bf100000)
concurrent mark-sweep generation total 14336K, used 2050K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3222K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 345K, capacity 388K, committed 512K, reserved 1048576K
可以发现在发生minor gc前,加入了3个2M,一个128k在eden里边,eden大小为8M,再加入2M就发送了minor gc,可以看到新生代内存从8144K减少到488K,整个堆内存占用从8144K减少到2538K,array1不引用的两个2M都被垃圾回收掉,新生代剩下488K(系统原本有的)放入的幸存区from,但是array1最后一个引用2M大于幸存区from(1M)直接进入老年代
发现老年代中占用2050K,正是最后一个array1引用的byte数组,在垃圾回收结束后,array3放入eden,幸存区放入448K
JVM 给每个对象定义了一个对象年龄计数器。当新生代发生一次 Minor GC 后,存活下来的对象年龄 +1,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中去。
使用 -XXMaxTenuringThreshold
设置新生代的最大年龄,只要超过该参数的新生代对象都会被转移到老年代中去。
public class mode_2 {
public static void main(String[] args) {
_2_year();
}
public static void _2_year() {
int _1MB = 1024 * 1024;
byte[] array1 = new byte[2 * _1MB];
array1 = new byte[2 * _1MB];
array1 = new byte[1 * _1MB];
byte[] array2 = new byte[128 * 1024];//经过三次GC后,想让这个128K进入老年代
array1 = null;
System.out.println("-----------1-----------");
byte[] array3 = new byte[2 * _1MB];//第一次minor gc,此时array2指向的对象0岁
array3 = new byte[2 * _1MB];
array3 = new byte[2 * _1MB];
array3 = null;
System.out.println("-----------2-----------");
byte[] array4 = new byte[2 * _1MB];//第二次minor gc,此时array2指向的对象1岁
array4 = new byte[2 * _1MB];
array4 = new byte[2 * _1MB];
array4 = null;
System.out.println("-----------3-----------");
byte[] array5 = new byte[2 * _1MB];//第三次minor gc,此时array2指向的对象2岁
}
}
jvm参数
-XX:NewSize=10m
-XX:MaxNewSize=10m
-XX:InitialHeapSize=20m
-XX:MaxHeapSize=20m
-XX:SurvivorRatio=6
-XX:PretenureSizeThreshold=10m
-XX:MaxTenuringThreshold=3 # 存活对象年龄超过3,则直接进入老年代
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintTenuringDistribution # 打印每次gc后幸存区内存大小、对象年龄阈值、存活对象大小、存活对象年龄
-XX:+PrintHeapAtGC # 每次gc前后打印堆内存占用情况
-XX:+PrintGCApplicationStoppedTime # 每次gc的stop the world时间
打印日志,缩略版
-----------1-----------
0.056: [GC (Allocation Failure) 0.056: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 602600 bytes, 602600 total
: 7160K->621K(8960K), 0.0005195 secs] 7160K->621K(23296K), 0.0005389 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-----------2-----------
0.057: [GC (Allocation Failure) 0.057: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 328 bytes, 328 total
- age 2: 545408 bytes, 545736 total
: 6992K->717K(8960K), 0.0004029 secs] 6992K->717K(23296K), 0.0004103 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
-----------3-----------
0.058: [GC (Allocation Failure) 0.058: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 192 bytes, 192 total
- age 2: 328 bytes, 520 total
: 7046K->161K(8960K), 0.0013712 secs] 7046K->710K(23296K), 0.0013797 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 8960K, used 2458K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 29% used [0x00000007be800000, 0x00000007bea3e4b0, 0x00000007bef80000)
from space 1280K, 12% used [0x00000007bf0c0000, 0x00000007bf0e85e0, 0x00000007bf200000)
to space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
concurrent mark-sweep generation total 14336K, used 548K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3231K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 345K, capacity 388K, committed 512K, reserved 1048576K
分析gc heap打印情况
根据我们设置启动的jvm参数,新生代为10M(eden7.5M,from 1.25M, to 1.25M),对象存活年龄阈值为2,Desired survivor size 524288bytes,当前
-----------1-----------
{Heap before GC invocations=0 (full 0):
par new generation total 8960K, used 7160K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 93% used [0x00000007be800000, 0x00000007beefe0c0, 0x00000007bef80000)
from space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
to space 1280K, 0% used [0x00000007bf0c0000, 0x00000007bf0c0000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3200K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
0.056: [GC (Allocation Failure) 0.056: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 602600 bytes, 602600 total
: # 7160K->621K(8960K), 0.0005195 secs] 7160K->621K(23296K), 0.0005389 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=1 (full 0):
par new generation total 8960K, used 621K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 0% used [0x00000007be800000, 0x00000007be800000, 0x00000007bef80000)
from space 1280K, 48% used [0x00000007bf0c0000, 0x00000007bf15b728, 0x00000007bf200000)
to space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3200K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
}
0.057: Total time for which application threads were stopped: 0.0006402 seconds, Stopping threads took: 0.0000152 seconds
-----------2-----------
{Heap before GC invocations=1 (full 0):
par new generation total 8960K, used 6992K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 82% used [0x00000007be800000, 0x00000007bee38c80, 0x00000007bef80000)
from space 1280K, 48% used [0x00000007bf0c0000, 0x00000007bf15b728, 0x00000007bf200000)
to space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3215K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K
0.057: [GC (Allocation Failure) 0.057: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 328 bytes, 328 total
- age 2: 545408 bytes, 545736 total
: # 6992K->717K(8960K), 0.0004029 secs] 6992K->717K(23296K), 0.0004103 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap after GC invocations=2 (full 0):
par new generation total 8960K, used 717K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 0% used [0x00000007be800000, 0x00000007be800000, 0x00000007bef80000)
from space 1280K, 56% used [0x00000007bef80000, 0x00000007bf0337f8, 0x00000007bf0c0000)
to space 1280K, 0% used [0x00000007bf0c0000, 0x00000007bf0c0000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3215K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K
}
0.058: Total time for which application threads were stopped: 0.0004802 seconds, Stopping threads took: 0.0000255 seconds
-----------3-----------
{Heap before GC invocations=2 (full 0):
par new generation total 8960K, used 7046K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 82% used [0x00000007be800000, 0x00000007bee2e0b8, 0x00000007bef80000)
from space 1280K, 56% used [0x00000007bef80000, 0x00000007bf0337f8, 0x00000007bf0c0000)
to space 1280K, 0% used [0x00000007bf0c0000, 0x00000007bf0c0000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3215K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K
0.058: [GC (Allocation Failure) 0.058: [ParNew
Desired survivor size 655360 bytes, new threshold 2 (max 2)
- age 1: 192 bytes, 192 total
- age 2: 328 bytes, 520 total
: # 7046K->161K(8960K), 0.0013712 secs] 7046K->710K(23296K), 0.0013797 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=3 (full 0):
par new generation total 8960K, used 161K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 0% used [0x00000007be800000, 0x00000007be800000, 0x00000007bef80000)
from space 1280K, 12% used [0x00000007bf0c0000, 0x00000007bf0e85e0, 0x00000007bf200000)
to space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
concurrent mark-sweep generation total 14336K, used 548K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3215K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K
}
0.059: Total time for which application threads were stopped: 0.0014371 seconds, Stopping threads took: 0.0000038 seconds
观察到eden空间充足,直接添加2M不会引起gc
Heap
par new generation total 8960K, used 2458K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 7680K, 29% used [0x00000007be800000, 0x00000007bea3e4b0, 0x00000007bef80000)
from space 1280K, 12% used [0x00000007bf0c0000, 0x00000007bf0e85e0, 0x00000007bf200000)
to space 1280K, 0% used [0x00000007bef80000, 0x00000007bef80000, 0x00000007bf0c0000)
concurrent mark-sweep generation total 14336K, used 548K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3231K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 345K, capacity 388K, committed 512K, reserved 1048576K
大对象是指需要大量连续内存空间的 Java 对象,如很长的字符串或数据。
一个大对象能够存入 Eden 区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及大量的复制,就会造成效率低下。
虚拟机提供了一个 -XX:PretenureSizeThreshold 参数,令大于等于这个设置值的对象直接在老年代分配,这样做的目的是避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制。(还记得吗,新生代采用复制算法回收垃圾)
public class mode_3 {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws InterruptedException {
byte[] array1 = new byte[2*_1MB];
byte[] array2 = new byte[3*_1MB];
}
}
jvm参数
-XX:NewSize=10m
-XX:MaxNewSize=10m
-XX:InitialHeapSize=20m
-XX:MaxHeapSize=20m
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=3m # 加入eden中的对象大于3M判定为大对象直接加入老年代
-XX:MaxTenuringThreshold=15
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintTenuringDistribution
-XX:+PrintHeapAtGC
-XX:+PrintGCApplicationStoppedTime
打印heap内存信息
可以发现现在新生代占用了4212K,包括我们自己创建的2M,剩下的都是系统启动自带的,还有3M被放入了老年代,这样验证了放入eden的对象大于等于3M就会被判定为大对象直接进入老年代(-XX:PretenureSizeThreshold=3m )
Heap
par new generation total 9216K, used 4212K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 51% used [0x00000007be800000, 0x00000007bec1d1f8, 0x00000007bf000000)
from space 1024K, 0% used [0x00000007bf000000, 0x00000007bf000000, 0x00000007bf100000)
to space 1024K, 0% used [0x00000007bf100000, 0x00000007bf100000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 3072K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3229K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 345K, capacity 388K, committed 512K, reserved 1048576K
如果当前新生代的 Survivor 中,相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄 >= 该年龄的对象就可以直接进入老年代,无须等到 MaxTenuringThreshold
中要求的年龄
先入为主,书本中是这样描述动态年龄判断的概念的
为了更好地适应不同程序的内存状态,虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
但是其实这种说法是不对的,以下代码会验证这种情况发生并且是如何解决的
public class mode_4 {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
byte[] array1 = new byte[2 * _1MB];
array1 = new byte[2 * _1MB];
array1 = new byte[1 * _1MB];
array1 = null;
byte[] array2 = new byte[300 * 1024];
System.out.println("------------1------------");
byte[] array3 = new byte[2 * _1MB]; // gc1
array3 = new byte[2 * _1MB];
array3 = new byte[2 * _1MB];
array3 = new byte[300 * 1024];
array3 = null;
System.out.println("------------2------------");
byte[] array4 = new byte[2 * _1MB]; // gc2
}
}
jvm参数
-XX:NewSize=10m
-XX:MaxNewSize=10m
-XX:InitialHeapSize=20m
-XX:MaxHeapSize=20m
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=10m
-XX:MaxTenuringThreshold=15
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintTenuringDistribution
-XX:+PrintHeapAtGC
-XX:+PrintGCApplicationStoppedTime
第一次垃圾回收新生代7420K减少到了794K,其中有300K我们引用的,还有494K是系统运行创建的,在经过第一次垃圾回收的时候,他们都被放在幸存区,age=1,此时Desired survivor size = 524288bytes,而当前幸存区存活的age =1 占用了780672,则新的threshold=1,在下一次垃圾回收时,会将age=1的直接进入老年代
------------1------------
{Heap before GC invocations=0 (full 0):
par new generation total 9216K, used 7420K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 90% used [0x00000007be800000, 0x00000007bef3f158, 0x00000007bf000000)
from space 1024K, 0% used [0x00000007bf000000, 0x00000007bf000000, 0x00000007bf100000)
to space 1024K, 0% used [0x00000007bf100000, 0x00000007bf100000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3221K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 344K, capacity 388K, committed 512K, reserved 1048576K
0.055: [GC (Allocation Failure) 0.055: [ParNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age 1: 780672 bytes, 780672 total
: # 7420K->794K(9216K), 0.0007712 secs] 7420K->794K(23552K), 0.0007927 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=1 (full 0):
par new generation total 9216K, used 794K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 0% used [0x00000007be800000, 0x00000007be800000, 0x00000007bf000000)
from space 1024K, 77% used [0x00000007bf100000, 0x00000007bf1c6b38, 0x00000007bf200000)
to space 1024K, 0% used [0x00000007bf000000, 0x00000007bf000000, 0x00000007bf100000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3221K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 344K, capacity 388K, committed 512K, reserved 1048576K
}
0.056: Total time for which application threads were stopped: 0.0009190 seconds, Stopping threads took: 0.0000282 seconds
新增4.3M后新生代占用了7398K,新加入的4.3M全部进入eden
在新增2M进入第二次minor gc,新生代内存占用减少到了37K,堆内存占用了759K,其中上一次gc age = 1 的 780772bytes直接进入了老年区,并且可以观察到当前幸存区有age = 1 占用了96bytes,并且new threshold = 15,恢复了原来的阈值,这说明
∑ a g e = 1 15 t a b l e S i z e [ a g e ] ( T a r g e t S u r v i v o r R a t i o ∗ S 0 / 100 ) \sum_{age=1}^{15} tableSize[age]( TargetSurvivorRatio * S0 / 100 ) age=1∑15tableSize[age](TargetSurvivorRatio∗S0/100)
公式在每次gc后都会进行动态计算,计算出如果(1)的结果>Desired survivor size( TargetSurvivorRatio * S0 / 100 ) = (50*1024/100)K*1024bytes = 524288,则会走min(age,MaxTenuringThreshold),重置new threshold的值
如果没有修改参数 -XX:TargetSurvivorRatio 则默认是50
jinfo -flag TargetSurvivorRatio PID # 可以使用jinfo -flag 参数名 进程号 进行验证
------------2------------
{Heap before GC invocations=1 (full 0):
par new generation total 9216K, used 7398K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 80% used [0x00000007be800000, 0x00000007bee730b0, 0x00000007bf000000)
from space 1024K, 77% used [0x00000007bf100000, 0x00000007bf1c6b38, 0x00000007bf200000)
to space 1024K, 0% used [0x00000007bf000000, 0x00000007bf000000, 0x00000007bf100000)
concurrent mark-sweep generation total 14336K, used 0K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3222K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 344K, capacity 388K, committed 512K, reserved 1048576K
0.056: [GC (Allocation Failure) 0.056: [ParNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 96 bytes, 96 total
: # 377398K->37K(9216K), 0.0016388 secs] 7398K->759K(23552K), 0.0016555 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap after GC invocations=2 (full 0):
par new generation total 9216K, used 37K [0x00000007be800000, 0x00000007bf200000, 0x00000007bf200000)
eden space 8192K, 0% used [0x00000007be800000, 0x00000007be800000, 0x00000007bf000000)
from space 1024K, 3% used [0x00000007bf000000, 0x00000007bf009520, 0x00000007bf100000)
to space 1024K, 0% used [0x00000007bf100000, 0x00000007bf100000, 0x00000007bf200000)
concurrent mark-sweep generation total 14336K, used 722K [0x00000007bf200000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3222K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 344K, capacity 388K, committed 512K, reserved 1048576K
}
0.058: Total time for which application threads were stopped: 0.0017115 seconds, Stopping threads took: 0.0000058 seconds
显然开头引用的 survivor区中,如果相同年龄的所有对象大小所占用的空间大于survivor空间的一半,年龄大于或等于该年龄对象的,都可以直接进入老年代
显然是错误的
应该是在survivor区中,所有年龄的对象的所占空间的累加和大于survivor空间的一半,大于或等于该年龄的对象,都可以进入老年代
在这里有关于下面这条公式的源码,有兴趣的小伙伴可以看看
∑ a g e = 1 15 t a b l e S i z e [ a g e ] ( T a r g e t S u r v i v o r R a t i o ∗ S 0 / 100 ) \sum_{age=1}^{15} tableSize[age]( TargetSurvivorRatio * S0 / 100 ) age=1∑15tableSize[age](TargetSurvivorRatio∗S0/100)
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
//survivor_capacity是survivor空间的大小
//desired_survivor_size就是动态年龄判断是否对象进入老年代的阈值
//TargetSurvivorRatio:默认50
size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
size_t total = 0;
uint age = 1;
while (age < table_size) {
//sizes数组是每个年龄段对象大小
//total就是年龄从小到大的对象占据的空间累加和
total += sizes[age];
//如果累加和大于阈值,就直接跳出循环,假设磁盘的age等于3吧
if (total > desired_survivor_size) break;
age++;
}
//MaxTenuringThreshold上限是15,现在age是3,那么result就是等于3
uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
...
}
以上内容仅为个人见解,如果有其他疑问,欢迎评论区留言