一入编程深似海,编程世界Very深。
这里是九神说编程,今天给大家说的是一个顶级大佬闲的无聊,在编程世界已经999级,闲来无事去新手村练小号的故事。
大佬本以为只要拿出0.001%的功力就可以轻松入职升级了,没有想到却遇上了变态面试官!
“看来,我需要使出我1%的功力了!”,大佬明悟道。
介绍一下作者本人,我虽名为九神,但在那场大战中连观战的资格都没有,以下的一切都是大佬小号口传于我的。而我,只是以第一人称口述,记录下了那场对决的万分之一!本次是第二面现场!
虽然上次我在基础面试中回答了
UTF-16是怎么实现Unicode编码的,以及UTF-8、UTF-16和UTF-32的选型
这种最最最基础的Java问题,但是我没有获得工作机会,此番升到2级之后,我获得了2面的机会。我要拿下offer!打怪升级!
他来了他来了,那个头发浓密,全身西服笔挺的30岁大叔面试官向我走来了!看着对方这张好像在我长远记忆中见过的帅气脸庞,我实在想不起来他是谁!
面试官笑呵呵的说道:“小伙子不错,居然升到了2级,我们还是先来几个简单的题目热热身吧!”
果然是简单的热身题目,但是我依然认真的回答道:大叔你好!,比较常见的有如下几种:
你让我答10个,那还不答上11个?肯定有加分啊!
可以通过System.getProperty(“sun.arch.data.model”)或者System.getProperty(“os.arch”)这两种办法在Java程序里获取JVM所运行的操作系统的是32位的还是64位。
HotSpot VM、J9 VM、Sun Classic VM、Exact VM、JRockit VM、Dalvik VM、Microsoft JVM、Azul VM、Liquid VM、Zing VM先说这些吧!
其中我们最主流用的是HotSpot VM,可以通过java -version查看你自己用的VM种类。
问常识的时候,可以多回答一点,代表我不是小白!
32 位的 JVM理论上堆内存可以到达 2^32, 即 4GB,但实际上会比这个小很多。
以Hotspot VM为例,在Windows下,最大的堆内存大约在1.5G左右;在基于较新的linux kernel的Linux系统下,其限制在2.5到3G之间,在早前的linux kernel下,大约在2G左右;在Solaris下,为3.3G左右。
64 位 JVM 允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。
他们的作用是,首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中。
而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,
而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
如果这个不懂,赶紧看下面的图,这个是真的很基本要会的!这里面有些题是专家级别的,但是这个是P5就要会的!
类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。
一旦一个类被加载入JVM中,同一个类就不会被再次载入了。正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识。
在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。
例如,如果在pg的包中有一个名为Student的类,被类加载器ClassLoader的实例ns负责加载,则该Student类对应的Class对象在JVM中表示为(Student.pg.ns)。
这意味着两个类加载器加载的同名类:(Student.pg.ns)和(Student.pg.ns2)是不同的、它们所加载的类也是完全不同、互不兼容的。
类加载器分为三类:
从类的生命周期而言,一般是加载、验证、准备、解析、初始化、使用和卸载这7个过程。
其中,加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序进行,
而解析阶段则不一定,它在某些情况下可能在初始化阶段后在开始,因为java支持运行时绑定。
一定要回答全面,很多人回答5个其实不会得到满分,而且必须理解每个名词是干嘛的
JVM的类加载机制主要有如下3种:
**全盘负责:**所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
**双亲委派:**所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,
如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
**缓存机制:**缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class。
只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。
这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。
Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。
为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器,英文名是Just In Time Compiler,也就是JIT。
而这种采用解释执行 + JIT(just-in-time 即时) 编译器编译热点的方式,就是HotSpot名字的来由。
就是要秀别人不知道的历史题,哈哈哈,就是要骚要秀,果然不愧是999级的小号
JIT有两种编译器:Client Compiler和Server Compiler。
其中Client Compiler,我们通常称为C1,这是一种轻量级编译器,特点是启动速度快,使用的场景是相比程序稳定时的运行速度,我们更在意内存占用空间要小。
而Server Compiler,我们通常称为C2,这是一种重量级编译器,使用的场景是相比内存占用空间,我们更在意程序稳定时的运行速度。
我们可以通过-client
来使用Client Compiler,通过-server
来使用Server Compiler。
在JDK7之后,我们就可以通过TieredCompilation这一分层编译模式,同时使用两种编译器,先使用C1达到启动时间短,而运行一段时间后,又唤起C2达到程序运行速度快的效果。
因为高P一般不写博客,所以这里国内的文章好像没有对的,经常面试很多人这里理解的一塌糊涂。尤其是那些认为Client的适用场景是客户端,Server是服务端的,这个理解不对,因为桌面应用未必轻量,比如视频类桌面应用。这里就能反映很多人不会看oracle的官方文档。
罕见陷阱是JIT这种典型的PGO(profile guide optimization)所带来的问题。
当JIT判断一段代码是热点代码后,但是下一次的行为和之前完全不同,这时候被已经编译的这个热点代码就完全无效了,这种行为就叫罕见陷阱。
当产生罕见陷阱之后,native code就会回滚(deoptimization)到解释器(interpreter),然后再次采集数据进行下一次优化。
面试官大叔看了一眼手腕上的百达翡丽,说时间不早了,要不先这样吧,让我回去等下次面试的通知。
我问道:那什么时候下次面试呢?
“等你升到3级的时候吧!下次还是JVM,这次过于简单了!”,面试官留下这就话就走了。
看不起小号么?还需要透漏下次还是JVM?来吧!大不了我给你写个比Hotspot还好的!(不,我不能透漏自己是999级小号的秘密!)
那大佬如何才能升级呢?据大佬透漏,需要有人给他的口述点赞达到一定数量方可升级,只有升级到3级,才能进入第三面!
这就是有幸聆听大佬口述,我来执笔写下这篇文章的缘由!
还不给点赞么?没下一篇了啊!
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是能进大厂的高端人才。
我后面会每周都更新几篇一线互联网大厂面试和常用技术栈相关的文章,非常感谢高端人才们能看到这里,如果这个文章写得还不错,觉得「九神说编程」有点东西的话 求点赞? 求关注❤️ 求分享? 对九神来说真的 非常有用!!!
注意:博文会比公号晚一周哦!
白嫖不好,创作不易, 各位的支持和认可,就是我创作的最大动力,我们下篇文章见!
给大家放个我做的长图,哈哈哈