没有合适的资源?快使用搜索试试~ 我知道了~
JVM大厂必备面试题八股文
需积分: 21 0 下载量 198 浏览量
2023-03-07
17:23:30
上传
评论
收藏 262KB PDF 举报
温馨提示
试读
12页
文档里有自己总结了大厂JVM常见面试题,自己凭借这套面试题拿下5-10个offer,目前已入职北京某互联网大厂,适合于正在找工作的同学。
资源推荐
资源详情
资源评论
内存结构
什么是Java虚拟机?为什么Java被称作是“平台⽆关的编程语
⾔”?
Java虚拟机是⼀个可以执⾏Java字节码的虚拟机进程。
Java被设计成允许应⽤程序可以运⾏在任意的平台,是因为Java虚拟机的存在。Java虚拟机让这个变为可能,因为
它知道底层硬件平台的指令⻓度和其他特性。
介绍⼀下JVM的内存结构?
按照线程来说分成两部分:⼀个是线程独占的,⼀个是线程共享的。
线程共享的:
Java堆:java虚拟机内存最⼤的⼀块,唯⼀⽬的就是存放对象实例。所有对象实例、数组都存放在java堆,也
是垃圾回收的主要区域,线程共享。
⽅法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。垃圾回收⽬标主要是常
量池的回收和类型的卸载,各线程共享。具体实现是元空间和永久代,永久代是JDK1.7的,元空间是JDK1.8
的,元空间存在于本地内存中,⽽不是虚拟机中。(永久代撤销后字符串常量池和静态变量都放⼊到了堆中)
运⾏时常量池。是⽅法区的⼀部分,⽤于存放编译期⽣成的各种字⾯量和符号引⽤。
直接内存:这部分不是运⾏时数据区的⼀部分
线程私有的:
程序计数器:当前线程所执⾏的字节码的⾏号指示器,字节码解释器⼯作时通过改变这个计数器的值来选取下
⼀条需要执⾏的字节码指令。多线程的情况下,程序计数器可以记录当前线程执⾏的位置,从⽽当线程被切换
回来的时候能够知道该线程上次运⾏到哪⼉了。
Java虚拟栈:它的⽣命周期和线程相同,描述的是 Java ⽅法执⾏的内存模型,每次⽅法执⾏都会创建⼀个栈
帧,⽤于存放局部变量表、操作数栈、常量池引⽤、⽅法出⼝等。
本地⽅法栈:和虚拟栈相似,只不过它服务于Native⽅法。
为什么要将永久代替换为元空间呢?
1. 整个永久代要设置固定⼤⼩上限,⽆法进⾏动态调整,永久代这种设计导致了Java应⽤更容易遇到内存溢出的
问题。⽽元空间使⽤的是直接内存,元数据就不由 MaxPermSize 控制了,⽽由系统的实际可⽤空间来控制,
这样能加载的类就更多了,更不容易溢出。
2. Oracle收购了BEA获得了JRockit的所有权后,要合并HotSpot和JRockit的代码,JRockit从来没有所谓的永久
代,也不需要开发运维⼈员设置永久代的⼤⼩,但是运⾏良好。
程序计数器为什么是私有的?
在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时候能够知道该线程上次
运⾏到哪⼉了。
所以,程序计数器私有主要是为了线程切换后能恢复到正确的执⾏位置。
虚拟机栈和本地⽅法栈为什么是私有的?
为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地⽅法栈是线程私有的。
本地⽅法栈与虚拟机栈的区别
本地⽅法栈与虚拟机栈所发挥的作⽤是⾮常相似的,其区别不过是虚拟机栈为虚拟机执⾏Java⽅法(也就是字节
码)服务,⽽本地⽅法栈则是为虚拟机使⽤到的Native⽅法服务。
字符串常量放在哪⾥?
在JDK1.7之前放在⽅法区(永久代)中
JDK1.7放到了堆中
JDK1.8之后,取消了永久代,运⾏时常量池和静态常量池还在⽅法区存放在元空间中,⽽字符串常量池依然
存放在堆中。
JVM 常量池中存储的是对象还是引⽤呢?
对象。 JDK1.8版本中,String常量池已经从⽅法区中的运⾏时常量池分离到堆中了。
Java中的对象⼀定在堆上分配内存吗?
不⼀定,
在编译期间,即时编译器会对代码做很多优化。其中有⼀部分优化的⽬的就是减少内存堆分配压⼒,其中⼀种重要
的技术叫做逃逸分析。
如果JIT经过逃逸分析,发现有些对象没有逃逸出⽅法,那么有可能堆内存分配会被优化成栈内存分配。
内存溢出和内存泄漏的区别
内存溢出 out of memory,是指程序在申请内存时,没有⾜够的内存空间供其使⽤,出现out of memory;
⽐如申请了⼀个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序中已经分配的内存由于某种原因没有释放或者⽆法释放,失去了对该段内
存的控制,因⽽造成了内存的浪费。
什么时候会出现栈内存溢出?
栈内存溢出是指线程请求的栈深度⼤于虚拟机所允许的最⼤深度,则将抛出StackOverflowError异常
(StackOverflowError 不属于 OOM 异常)。最有可能的原因就是⽅法递归产⽣的这种结果。线程执⾏过程中栈帧
⼤⼩超出虚拟机栈限制。
另⼀个可能是引⽤了⼤的变量,在动态扩展栈时⽆法申请到⾜够的内存空间,则抛出OutOfMemoryError异常(这
个属于内存溢出)。
当出现了内存溢出,怎么排错。
⾸先控制台查看错误⽇志。
然后使⽤jdk⾃带的VisualVM来查看系统的堆栈⽇志(也可以⽤jmap查看堆转储快照)。
定位出内存溢出的空间:堆,栈还是永久代(jdk8后没有永久代的溢出了)。
如果是堆内存溢出,看是否创建了超⼤的对象。
如果是栈内存溢出,看是否创建了超⼤的对象,或者产⽣了死循环,或者递归调⽤。
对象的访问定位⽅式有⼏种,分别介绍⼀下。
两种,分别是句柄和直接指针。
如果使⽤句柄访问的话,Java堆中将可能会划分出⼀块内存来作为句柄池,reference中存储的就是对象的句柄地
址,⽽句柄中包含了对象实例数据与类型数据各⾃具体的地址信息。
如果使⽤直接指针访问的话,Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference
中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多⼀次间接访问的开销。
说⼀下对象创建的过程。
1. 类加载检查
虚拟机遇到⼀条new指令时,⾸先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引⽤,并且
检查这个符号引⽤代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执⾏相应的类加载过程。
2. 分配内存
在类加载检查通过后,接下来虚拟机将为新⽣对象分配内存。分配⽅式有 “指针碰撞” 和 “空闲列表” 两种,选
择那种分配⽅式由 Java 堆是否规整决定(规整⽤指针碰撞,不规整⽤空闲列表),⽽Java堆是否规整⼜由所采⽤
的垃圾收集器是否带有压缩整理功能决定。
3. 初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值
4. 设置对象头
初始化零值完成之后,虚拟机要对对象进⾏必要的设置,例如这个对象是那个类的实例、如何才能找到类的元
数据信息、对象的哈希码、对象的 GC 分代年龄等信息,这些信息存放在对象头中。
5. 执⾏init⽅法
使⽤构造⽅法初始化对象。
Java中的对象创建有多少种⽅式?
4种⽅式可以创建⼀个对象。
new关键字
反射机制
使⽤clone⽅法
反序列化的⽅式
剩余11页未读,继续阅读
资源评论
编程芝士
- 粉丝: 2w+
- 资源: 15
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功