06 “eden没有发生minor gc, 对象直接分配在了old gen“ 的调试

2024-05-28 15:32

本文主要是介绍06 “eden没有发生minor gc, 对象直接分配在了old gen“ 的调试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

呵呵 最近在看这样一篇文章的时候, eden区没有发生minor gc,对象直接分配在了old gen 

看到了 R大 的叱咤风云, 讲解的非常细致, 十分令人佩服, 然后 若是想有所收获, 还得 构造一下这个情况, 复现一下, 然后 调试着走一次, 才能 有所收获, 嘿嘿 

当然 由于 vm 版本不一样, 因此 下面的测试用例的相关 选项 我这里做了一些 调整 

 

一下代码, 截图 基于 jdk9 

 

 

测试用例如下 

package com.hx.test04;/*** AllocationTest** @author Jerry.X.He <970655147@qq.com>* @version 1.0* @date 2020-03-06 15:24*/
public class Test03AllocationTest {/*** constants*/private static final int _1KB = 1024;private static final int _1MB = _1KB * 1024;// Test03AllocationTest// refer : https://hllvm-group.iteye.com/group/topic/38293/*** -Xint -Xmx100M -XX:+UseParallelGC -XX:-UseTLAB -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:NewSize=28M*/public static void main(String[] args) {testAllocation();}// testAllocationpublic static void testAllocation() {byte[] byte1 = new byte[_1MB*5];byte[] byte2 = new byte[_1MB*10];byte1 = null;byte2 = null;byte[] byte3 = new byte[_1MB*5];byte[] byte4 = new byte[_1MB*10];byte3 = null;byte4 = null;byte[] byte5 = new byte[_1MB*15];}}

跑的时候 需要加上 如上的相关 vm 参数, 但是 普通的 跑这个 测试用例 看不到太多的东西, 需要结合 具体的调试, 才能 了解到 R大 所说的一系列描述的意思  

 

 

第一个字节数组的数据分配 

可以看到, 这里是 第一个 "new byte[_1MB*5]", length 为 5M, size 怎么看起来和 length 没什么关系啊 ? 
呵呵 我最开始看到这里也奇怪, 直到看了一下 "size_t size = typeArrayOopDesc::object_size(layout_helper(), length);" 的代码 

原来这里的 size = (length + header_size) / WordPerBytes 计算出来的, BytesPerWord 是指一个字对应多少个字节, 64位操作系统中其值为 8 

呵呵 我最开始看觉得有些奇怪是因为 我发现这个 size 怎么比 length 大这么多??, 不就装字节数据 + mark + klass + length 么, 咋个会多出这么多, 后来仔细一看 原来 size 是比 length 少了一位 

 

 

第一次字节数组的分配细节 

分配之前 young_gen 的 used 为 4.18M, capacity 为 24.50M(eden:21M, from:3.5M)

eden 的 used 为 4.18M, capacity 为 21M 

我们这里分配的是 5M, 可以分配, 就直接从 young_gen 里面分配了 

 

创建第一个字节数组之后 young_gen used 为 9.18M 

 

 

第二次数组分配的细节 

分配之前 young_gen 的 used 为 9.18M, capacity 为 24.50M(eden:21M, from:3.5M)

eden 的 used 为 9.18M, capacity 为 21M 

堆的相关数据 和 第一次数组分配之后的结果一样, 只是这里需要分配的字节数组 变成了 10M 

这里 young_gen 依然能够分配第二个数组 

 

分配之后 young_gen used 为 19.18M 

 

 

第三次数组分配的细节  

分配之前 young_gen 的 used 为 19.18M, capacity 为 24.50M(eden:21M, from:3.5M)

eden 的 used 为 19.18M, capacity 为 21M 

到这里 young_gen 分配不了 这个 5M 的字节数组了 

 

后面尝试 在 young_gen 再重新分配, 失败 

尝试在 old_gen 分配空间, 不满足条件 

gc_locker_stalled_count, GCLocker::is_active_and_needs_gc 也不满足条件 

 

尝试在 old_gen 分配空间的条件如下 

这里的 GCLocker::is_active_and_needs_gc 和 _death_march_count 在这里不满足条件 

 

R大 评论中也是这个判断, 只是由于 版本不一样, 细节上存在一些 差异 

 

然后触发了一次 minor gc 

 

VM_ParallelGCFailedAllocation 处理了之后 为第三个字节数组 分配了空间 

分配之后 eden 的 used 为 5.00M 

 

 

第四次数组分配细节如下 

分配之前 young_gen 的 used 为 5.00M, capacity 为 24.50M(eden:21M, from:3.5M)

eden 的 used 为 5.00M, capacity 为 21M 

可以明显看到 eden 是足以分配 这个 10M 的字节数组的, 因此这里 就在 young_gen 分配 

 

分配之后 eden 的 used 为 15.00M 

 

 

第五次数组分配的细节

分配之前 young_gen 的 used 为 15.00M, capacity 为 24.50M(eden:21M, from:3.5M)

eden 的 used 为 15.00M, capacity 为 21M 

在 young_gen 已经分配不下 这里的 15M 的数组 

 

根据上面的 是否需要在 old_gen 分配的条件之一, "size > eden_space.capacity_in_words/2", 来比较的话 

我们这里 size 是 1966082, eden_space.capacity_in_words/2 为 1376256, 是符合 在 old_gen 分配的条件的, 因此 这里第五个 字节数组 就在 old_gen 分配了 

这上面的 比较还可以换一个方式, 上面的比较的单位是 字, 我们换成字节的话 会更清晰一些, eden 的空间是 21 M, 一半为 10.5M, 然后 这里申请的空间为 15M, 15M > 10.5M, 因此 可以在 old_gen 里面分配 

 

 

完 

 

 

引用

eden区没有发生minor gc,对象直接分配在了old gen

这篇关于06 “eden没有发生minor gc, 对象直接分配在了old gen“ 的调试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010965

相关文章

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

解读GC日志中的各项指标用法

《解读GC日志中的各项指标用法》:本文主要介绍GC日志中的各项指标用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基础 GC 日志格式(以 G1 为例)1. Minor GC 日志2. Full GC 日志二、关键指标解析1. GC 类型与触发原因2. 堆

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA