开足码力,码动人生,微信搜索【 程序员大帝 】,关注这个一言不合就开车的的代码界老司机
本文 GitHub上已经收录 https://github.com/BeKingCoding/JavaKing , 一线大厂面试核心知识点、我的联系方式和技术交流群,欢迎Star和完善
一个一看就是一周没洗头,穿着格子衬衣的中年男子,拿着背面满是贴纸的 mac 走了进来。看着这地中海,心想他肯定是尼玛高级架构师吧!但是看过程序员大帝的小伙伴,肯定都跟无忌一样练就了吹的能力,肯定不带虚的。
小伙子您好,看你简历上写了你对 JVM 的原理和调优都有所了解,先给我讲讲你对 JVM 的了解?
心里忍不住暗骂,这叫啥问题,太宽了吧,但是你不能说出来。这个问题和自我介绍一样,就是抛砖引玉。
所以咱们还是要认真回答:Java 虚拟机是 Java 语言能够得到广泛传播的基础,让 Java 一处安装,到处运行成为了可能。在不同平台上运行时不需要重新编译,通过 JVM 屏蔽了与具体平台相关的信息,使咱们 Java 程序员只需要关注目标代码的实现即可。
JVM 有哪几部分呀?
堆 Heap、栈 Stack、程序计数器 Register、方法区/永久代 Permanent、本地方法栈 Native Method Stack。
这里我相信 99% 的读者都能回答上来 JVM 的 5 个基本组成部分,如果回答不出来的小伙伴我们就要加油补课哟,大家知道这五个部分每个的作用更好。
但是,如果你还想进一步突出你和其他候选人的不同,还需要加上下面的一些知识点。
在 JDK8 之后,对 JVM 内存空间开始了改造,主要的区别是将方法区进行了移除,并新增了元空间,元空间是放置在 JVM 内存空间之外的直接内存中,并且 JDK8 中对于方法区的参数 PermSize 和 MaxPermSize 已经失效。
不错,那为什么需要这样改造?元空间是什么结构呢?
有的时候面试官其实也不知道怎么问,所以我们需要对面试官进行引导。比如通过上一个问题,我们就可以引出自己对元空间的理解,这个知识点其实是非常能体现能力的。
在 JDK8 之前,JVM 模型中存在着方法区,它是一个非常重要的区域,与堆一样可以被线程共享。
在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。我们也习惯称方法区它为“永久代”(Permanent Generation),但是绝大部分 Java 程序员应该都见过“java.lang.OutOfMemoryError: PremGen space”异常,这里的“PermGen space”其实指的就是方法区。
通过 -XX:MaxPermSize 这个参数可以设定方法区的大小,但是为它到底分配多大的空间很难确定,因为PermSize的大小依赖于很多因素,比如JVM加载的class总数,常量池的大小,方法的大小等。
随着动态类加载的情况越来越多,这块内存越来越不太可控。如果设置小了,当JVM加载的类信息容量超过了这个值,系统运行过程中就容易出现内存溢出OOM:PermGen 的错误,设置大了又浪费内存。
而元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间的最大区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
注:本人在面试时非常喜欢问这个问题,真正能把这其中的原理说清楚的同学不多,但其实很好理解,看完这篇文章相信你就可以在面试官面前侃侃而谈了。
刚刚你提到了内存溢出,可不可以说几种常见的 OOM?
有的同学看到这个问题,心想这不是给自己挖坑么,我知道 OOM 是 Out Of Memory 内存溢出错误,哪里还知道其中的道道呢?
但大家遇到凡事不要慌,屡屡思路问题不大。
当JVM内存严重不足时,就会抛出 java.lang.OutOfMemoryError 错误,根据实际生产经验,OOM是非常严重的问题,一般会对程序日志中的 OutOfMemoryError 配置关键字告警,一经发现,立即处理。
常见引起 OOM 的错误主要有以下几种:
(1)Java Heap Space
当堆没有足够的内存空间存放新创建的对象时,就会抛出 java.lang.OutOfMemoryError:Java heap space 错误。比如请求创建一个超大的大数组对象,或者发生了内存泄露,该回收的对象没有释放,堆空间不足等等。
(2)GC overhead limit exceeded
当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 错误。
(3)Unable to create new native thread
Java 的线程是用户级线程,它的创建和使用对应着操作系统的内核级线程,每个 Java 线程都需要占用一定的内存空间。当 JVM 向底层操作系统 OS 请求创建 native 线程,没有足够的资源分配就会报此类错误。
这时候估计面试官会露出一个(猥琐)的笑容,心想今天面试的这个小伙子还不错,掌握的很扎实。下面要问些有难度的问题了,看看他到底什么水平。
平时项目中有没有 JVM 调优的经验呢?
无形装逼,最为致命。而这个问题,就是给你吹牛*的最好机会!
很多同学都会觉得 JVM 是底层知识,掌握起来非常困难,但这是成为一名优秀 Java 程序员的必修课!
这时候即使你内心汹涌澎拜,也要表面给予恰当其份的淡然,然后回答:嗯,做过一些,项目上线前的 JVM 优化和线上问题排查都经历过。
对方包括我这时都会非常有兴趣,心里开始默念:终于开始有点意思了。
毕竟前面的问题都是偏向于理论的知识,但是涉及到 JVM 调优和问题排查就会考察具体的功底了,光靠背概念可没用。
你是怎么了解 JVM 运行的状况?
很多成熟的公司都已经建立了可视化的监控平台,比如Zabbix、Ganglia、Open-Falcon、Prometheus 等等,但如果没有这样的条件,通过命令行我们也可以观察 JVM 各个区域的内存变化,GC 次数和 GC 耗时。
jstat 就是自带的小而强大的工具,可以直观的把每一个参数打印出来。具体的使用大家可以看之前的文章:
《大杀器jstat,再也不怕 JVM 发脾气了》
如果发现内存溢出了,怎么处理?
发生了内存溢出,我们需要先判断具体是哪一种 OOM,然后再做相应处理。比如堆内存溢出,有可能是因为大对象导致的,这个时候我们可以通过 jmap 工具,dump 出内存快照,然后再用 jhat 或者 Visual VM 之类的可视化工具来分析就可以了。
之前线上出过一个真实的重大Bug《重大事故!线上系统频繁卡死,凶手竟然是 Full GC ?》,大家可以学习一下分析和排查思路。
面试的时候,完全可以把这个当成你自己的真实经历,和面试官侃侃而谈。相信到这里面试官暗地里已经对你竖起了大拇指。并且默默给了你A+。
小伙子你可以的,什么时候有时间来上班啊,要不明天就来吧?
你强装镇定,这么急啊我还需要考虑考虑,要不下礼拜一吧。
好的,心想这小子这么龟毛是不是很多 Offer 在手上,不行我得叫 hr 小姐姐给他加钱还得微信沟通下。
能撑到最后,你自己都忍不住自己给自己点个赞了。其实在技术面试的时候,不管是 JVM 还是其他问题,如果你能举出实际的例子,或者是自己开发过程的问题和收获都会给面试官很好的印象,回答逻辑性也要强一点,不要东一点西一点。你想面试官可能刚刚还在修bug,把你自己和他都绕晕的。
还有就是回答问题的时候不要背概念,大家都这么回答,面试官肯定也想听点不一样的东西,比如你可以这样回答:
大佬您好,首先我们的项目在上线之后遇到了性能瓶颈,特别是在有秒杀活动的时候。经常由于数据量太大会报 JVM OOM 的错误,所以需要对它进行排查和调优,排查我是从观察对象分布、垃圾回收耗时…入手,调优我是从调整内存大小、更改垃圾回收次数…进行,综合这些然后再结合我们项目特点,最后我在线上做了…。
如果你这样有条不紊,有理有据的回答了我的问题而且还说出很多问题外的知识点,我会觉得你不只是一个会写代码的人。你逻辑清晰,对知识有自己的理解和思考,说白了就是你的offer有戏了。
《Offer收割机》系列持续更新,也会定期分享互联网常用技术栈相关的文章,GitHub 上已经收录 https://github.com/BeKingCoding/JavaKing ,讲解一线大厂面试要求的核心知识点、并有对标阿里P7级别的成长体系脑图,欢迎加入技术交流群,我们一起有点东西。
我是一言不合就开车的代码界老司机无忌。创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!