JVM面试热点1
1.运行时数据区
线程共享:方法区、堆
线程独享:虚拟机栈、本地方法栈、程序计数器
2.程序计数器,是java虚拟机设计规范中的一个规定。if else、break等控制执行流程的地方。这是唯一一个没有OOM的地方,因为只记录一个行号。
3.java虚拟机栈:将方法的局部变量生成一个栈帧。
两种异常:请求的栈深度大于允许的栈深度。
-
栈溢出(递归)
-
有些jvm允许栈大小扩充,但是一直扩充电脑的内存也不够了就会产生OOM异常。(概率很小,电脑内存一般足够支持jvm的栈)
本地方法栈:如果java程序需要调用c程序,c程序不归jvm管了。但是java程序需要传参,交给虚拟机栈处理。很多虚拟机将本地方法栈和虚拟机栈合二为一。
-
堆,线程共享
(1)考虑一个问题,线程共享,那堆的空间怎么分配?
每个线程都有一个线程私有的分配缓存区,就是一块小空间,先让这个线程用着。
堆逻辑上连续,物理地址可以不连续。堆可以设置成可以拓展的,有参数控制。内存不够,报错OOM。
5.方法区、线程共享
存放类信息、静态变量、常量的区域,可能会OOM
2.创建对象
(1)创建前就确定对象的大小,堆中怎么给对象分配空间?
指针碰撞:在用过的位置末尾之后移动一个空间,给新对象。适用于内存规整的。
空闲链表法:
(2)用上面的那个方法,垃圾收集器,是否带有垃圾整理的功能。
7.对象申请堆空间时,两个同时申请。并发异常?
(1)cas+失败重试
(2)tlab:每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲。本地缓冲用完了,分配新的缓存区采用同步锁定。
3.堆中对象存的结构
- 对象头:使用标志位表现区分。类似100¥ 100$ 的区别。记录的信息包括:hash码、GC分代年龄、锁
- 实例数据:类型指针、数组长度
- 对齐填充:寻址必须是8字节的整数倍,这个填充是为了满足这个规定。
4.对象的访问定位
(1)句柄访问:
(2)直接指针(hotspot采用的这个)
优缺点:
(1)对象移动时。句柄引用:在堆中内存发送变化。只需要修改句柄的指针地址就行。如果是直接引用,好几个对象都引用了堆中的实例,reference都需要修改。
(2)但是句柄引用在定位对象时,显然需要一个句柄媒介。速度比直接引用慢。
5.垃圾回收(针对堆和方法区)
那些内存需要回收、什么时候回收、怎么回收
(1)判断对象已死?
引用计数法:在对象头添加引用它的指针个数。问题是:循环引用。
可达性分析:(java、c#)所有对象以GCROOT为根,连接起来了就不用回收
(2)引用
- 强:即使OOM也不回收
- 软:内存溢出前回收
- 弱:只要垃圾收集就死
- 虚:对垃圾收集没关系,只有得到通知(插眼,也操作不了对象、只能看到它还活着)
类卸载需要将类加载器也回收
6.对象回收
(1)假说:
- 弱分代假说:所有对象都是朝生夕灭
- 强分代假说:(可能是GCROOT)活的越久就活的越久
(2)针对这些假说,将对象分类分区存储。弱分代,扫描高频。强分代,扫描低频。
-
新生代:死的快的
-
老年代:活的久的
(3)对象的跨代引用(老年代对象指向新生代对象)?
相互引用的对象,如果新生代也一直没被回收,也放到老年代。
(4)算法
标记清除算法:第一轮打标记,第二轮清除(效率不稳定、空间碎片化;只清除不填充)
标记复制算法:为了解决有大量可回收对象效率低的情况。将空间分成A和B两块区域。A用完了,将还存在的对象复制到B。(只利用一半空间,如果存活对象比较多,来回复制开销大)。新生代划分成:伊甸园8 幸存1 幸存1比例,每次例如90%的空间。问题是保证不了这10%能存下所有幸存的对象。解决:放不下的仍老年代。
标记整理算法:针对如果存活的对象很多,标记复制效率很低。第一轮先打标,第二轮将存活的移动到开头排好。移动对象时什么也不能干(stop the world)
分代算法:将对象分代存放,按照各自不同的特点选择合适的回收算法。
原文地址:https://blog.csdn.net/m0_63803244/article/details/140402791
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!