内容简介:一般来我们公司面试,我都会习惯性地问点JVM相关的问题,当然如果他觉得JVM掌握得不错,我会适当多问点,毕竟知音难觅,难得在这么一条”狭路上相逢”。(如果你想在技术深度上有所积累,可以来我们公司一起努力,有兴趣的可以发简历给我们—比如今天要说的这个问题,就是我经常问的一个问题,只是和我之前排查过的场景有些区别,属于另外一种情况。也许我这里讲了这个之后,会成为不少公司JVM必问之题,所以本文还是值得大家好好看看的,相信也会让你很有收获,我把这个问题简单归纳为昨天美团(话说美团的小伙伴都很爱思考
概述
一般来我们公司面试,我都会习惯性地问点JVM相关的问题,当然如果他觉得JVM掌握得不错,我会适当多问点,毕竟知音难觅,难得在这么一条”狭路上相逢”。(如果你想在技术深度上有所积累,可以来我们公司一起努力,有兴趣的可以发简历给我们— hr@perfma.com
)。
比如今天要说的这个问题,就是我经常问的一个问题,只是和我之前排查过的场景有些区别,属于另外一种情况。也许我这里讲了这个之后,会成为不少公司JVM必问之题,所以本文还是值得大家好好看看的,相信也会让你很有收获,我把这个问题简单归纳为 Hotspot GC研发工程师也许漏掉了一块逻辑
。
昨天美团(话说美团的小伙伴都很爱思考 假笨说-协助美团kafka团队定位到的一个JVM Crash问题 )的一个小伙伴在群里(我们的JVM群,有兴趣的可以加我微信拉你入群 han_quanzi
)问了一个问题,如下图所示,在上一次YGC之后,from space的使用率是12%,但是在下一次YGC准备发生的时候,发现from space的使用率变成了99%。
OK,看到这里,请停下来思考10秒钟,想想这个现象是否正常。
-
如果你觉得这个现象不正常,说明你对JVM内存分析有一定的理解,但还是没有完全理解。
-
如果你觉得这个现象没问题,绝大部分说明你对JVM内存分配还不够熟悉,极少部分情况说明你对它已经非常熟悉了,对它实现上的优缺点都了如指掌了。
那请问你是属于哪种呢?
其实简化下来的问题就是:
非GC过程中,新创建的对象可能在from space里分配吗?
JVM内存分配
JVM内存分配说简单也简单,说复杂也复杂,不过我这里不打算说很细,因为要扯开讲,基本可以讲几个小时,我这里只挑大家熟知的来聊。暂时把大家归结为上面的第一种情况。
大家知道Java Heap主要由新生代和老生代组成,而新生代又分别由eden+s0(from space)+s1(to space)构成,通常情况下s0或者s1有一块是空的,主要用来做GC copy。
当我们创建一个对象的时候,会申请分配一块内存,这块内存主要在新生代里分配,并且是在eden里分配,当然某些特殊情况可以直接到老生代去分配,按照这种规则,正常情况下怎么也轮不到到from space去分配内存,因此在上次GC完之后到下次GC之前不可能去from space分配内存。
事实是怎样呢
那到底是不是这样呢?从上面的GC日志来看显然不是这样,我之前有过一次经验是这种情况和GC Locker有关,当时在群里要美团的同学把后面的日志发全一点验证下,结果比较意外,不是我之前碰到的情况,因此我要了整个完整的GC日志,下面简单描述下我对这个问题的思考过程。
我拿到GC日志后,第一件事就是找到对应的GC日志上下文,这种诡异的现象到底是偶尔发生的还是一直存在,于是我整个日志搜索 from space 409600K, 99%
,找到第一次情况发生的位置,发现并不是一开始就有这种情况的,而是到某个时候才开始有,并且全部集中在中间某一段时间里,那我立马看了下第一次发生的时候的上下文,发现之前有过一次Full GC和一次CMS GC
再找了最后一次发生的时候后面的情况
发现也有次Full GC,那综合这两种情况,我基本得出了一个大致的结论,可能和Full GC有关。
源码验证
带着疑惑我开始找相关源码来验证,因为我知道有从from space分配的情况,于是直接找到了对应的方法
从上面的代码我们可以做一些分析,首先从日志上我们排除了GC Locker的问题,如果是GC Locker,那在JDK8下默认会打印出相关的cause,但是实际上gc发生的原因是因为分配失败所致,于是重点落在了 should_allocate_from_space
上
bool should_allocate_from_space() const { return _should_allocate_from_space; }
那接下来就是找什么地方会设置这个属性为true,找到了以下源码
这是gc发生之后的一些处理逻辑,并且是full为true的情况,那意味着肯定是Full GC发生之后才有可能设置这个属性 set_should_allocate_from_space()
,并且也只有在Full GC之后才可能清理这个属性 clear_should_allocate_from_space()
,那基本就和我们的现象吻合了。
那是不是所有的Full GC发生之后都会这样呢,从上面的代码来看显然不是,只有当 !collection_attempt_is_safe() && !_eden_space->is_empty()
为true的时候才会有这种情况,这里我简单说下可能的场景,当我们因为分配内存不得已发生了一次Full GC的时候,发现GC效果不怎么样,甚至eden里还有对象,老生代也基本是满的,老生代里的内存也不足以容纳eden里的对象,此时就会发生上面的情况。
不过随着时间的推移,有可能接下来有好转,比如做一次CMS GC或许就能把老生代的一些内存释放掉,那其实整个内存就又恢复了正常,但是这带来的一个问题就是发现后面经常会发生从from space里分配内存的情况,也就是我们这次碰到的问题,直到下次Full GC发生之后才会解封,所以我们哪怕执行一次 jmap -histo:live
也足以解封。
GC研发工程师漏掉的逻辑?
那这样其实带来了一个新的问题,那就是会让更多的对象尽快晋升到老生代,这会促使老生代GC变得相对比较频繁,我感觉这其实应该算是JVM的一个bug,或许更应该说是GC研发工程师不小心漏掉了一块逻辑。
我觉得一个合理的做法是如果后面有CMS GC,那在CMS GC之后,应该主动 clear_should_allocate_from_space()
,也就是在CMS GC的sweep阶段执行完之后执行上面的逻辑,这样就会有一定保证,事实上,我们从sweep的源码里也看到了部分端倪,最后调用了 gch->clear_incremental_collection_failed()
,所以我个人以为是Hotspot GC开发的同学忘记做这件事情了,只需要在 gch->clear_incremental_collection_failed()
后面调用新生代的 clear_should_allocate_from_space()
即可解决此类问题
结语
至此应该大家知道问题答案了,实际上是可能在from space里直接分配对象的,但是现在的实现可能存在一些问题会导致老生代GC变得频繁。到底这个问题要不要提给OpenJDK社区呢,不过我的老团队阿里AJDK,这个问题我觉得应该可以修复一下吧,哈哈哈。
对了,大家如果有GC上的有意思的现象都可以发邮件给我,我很乐意和大家一起一探究竟。
大家也可以关注我们公司PerfMa的微信公众号,后面会给大家带来更多有意思有深度的技术文章。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 讲讲大厂面试必考的假设检验
- javascript面试必考知识点
- Go 面试必考题目之 method 篇
- Go面试必考题目之slice篇
- 前端面试必考题:React Hooks 原理剖析
- 这十几个链表题型面试必考(详细解析)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数学建模(原书第5版)
[美] Frank R. Giordano、[美] William P.Fox、[美] Steven B.Horton / 叶其孝、姜启源 / 机械工业出版社 / 2014-10-1 / 99.00元
《华章数学译丛:数学建模(原书第5版)》旨在指导学生初步掌握数学建模的思想和方法,共分两大部分:离散建模和连续建模,通过本书的学习,学生将有机会在创造性模型和经验模型的构建、模型分析以及模型研究方面进行实践,增强解决问题的能力。 《华章数学译丛:数学建模(原书第5版)》对于用到的数学知识力求深入浅出,涉及的应用领域相当广泛,适合作为高等院校相关专业的数学建模教材和参考书,也可作为参加国内外数......一起来看看 《数学建模(原书第5版)》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
随机密码生成器
多种字符组合密码