JVM疑难问题

数组类创建过程

【结论】数组类本身并不是由类加载器负责创建的,而是由JVM直接在内存中动态构造出来的

理解:
String[] Student[] Integer[] int[]

【结论】数组不是类加载器创建的而是java虚拟机直接创建的,知道这个结论就可以了,因为数组不属于类

165547629342870

核心就是:
创建数组类的过程:
1、如果数组的元素类型是引用类型,那么就遵循定义的加载过程递归加载和创建数组的元素类型。
2、JVM使用指定的元素类型和数组维度来创建新的数组类。
引导类加载器,又叫启动类加载器,就是最核心的类加载器。

复制算法特殊情况

165547804506153
【问题】复制算法这里说S1空间已满就进入老年代,但是不应该是每次GC存活下来的对象年龄加一,当达到一个阀值的时候才进入老年代否则会再次在新生代GC吗
【核心思路】
这个上面说的是一种特殊情况,你理解着就行,面试不需要这么说;jvm内部会有很多优化措施,你理解这个就是一种优化措施就行,这个只是理解
【问题1】
那每次new一个对象是只在eden区中存放吗,还是S0或S1也会有
【答案】
一般情况下都是在eden创建的,只有经过新生代minor gc才会进入s0或s1,不会在so或s1新建
【问题2】
165547829264612
那这里的垃圾对象是指第二次GC时会被清除的对象,对吧?
【答案】
对的,先清除不可达的,然后把eden和s0的都复制到s1,然后so和eden就清空了

类的不同版本相关问题

165547861259638
【问题1】
老师这个有什么示例吗?
【答案】
这个说的是纯理论,你理解就行,实际项目往往要避免的是同一类的不同版本,因为你想,除了写这个类的,后面的接手的不知道这个情况Bug都没法修,这属于不符合开发规范的。这块面试的时候最好别提

System.gc()

【问题1】System.gc()算手动回收对象吗?还有别的方式可以手动回收对象吗?
虚、弱引用算是吗?
【答案】
���个是给垃圾回收进程建议,让垃圾回收进程回收,jvm中没有手动回收的。
你即使设置=null也是让不可达,啥时候回收是垃圾处理进程决定的

虚、弱只是确定回收的优先顺序,这个就是纯理论,知道就行,实践中没人这么用。

jvm中垃圾回收是单独进程,而且垃圾回收的时候用户线程是暂停的,所以jvm中就没有设置手动回收垃圾的机制,要不跟前面的暂停就冲突了,你可以这么简单理解,实际面试有人问也是问结论,这块不会有人深入问的。

栈的OOM相关问题

【问题1】栈OOM是怎么产生的呢?
【答案】
比如有慢代码,导致每个接口要1分钟才能返回,压力一来,又没有设置最大线程池,线程很多,是不是就OOM了

比如递归没写结束条件,就会发生栈溢出

【问题2】如何判断堆OOM的原因是内存溢出还是内存泄露?
【答案】
内存溢出和泄漏表象有可能不一样,一般来说OOm都是溢出,java进程还在但是已经不响应了,内存泄漏可能进程不存在(被操作系统kill掉了),当然,没那么绝对,你现在建议只回答理论和可能是什么原因导致的问题就行。

比如OOM一般是死循环或者创建大量的对象导致的,内存泄漏是该回收的未回收,或者直接内存使用有错误,比如Nio里面异常了等等。

嗯嗯,你重点还是说理论,你的理解,解决可以说个思路就行,具体实践没用过,面试就这么答复,面试官问为什么学自己能说出所以然,比如觉着对工作有用等等就行

构造块与构造方法

165564250664183

【问题】为什么这样括起来就是 构造快 呢?
【答案】这个就是语法定义的,{}定义的,在方法外面的,就叫构造块,每次执行构造方法前先执行构造块

内部局部变量

165564258331261
【问题】如果不加int,是不是就 赋值 了
【答案】 回答正确

GCRoot相关的理解

165564264410405

【说明】

  1. 是根据可达性算法判断的,只要不可达就会回收;栈是线程私有的,线程结束栈就会销毁,这个时候栈引用做未GcRoot的对象都不可达了;
  2. 静态变量引用的一般都可达,方法区的方法一般也都不会销毁,但是出现full Gc了就会销毁,这回对象都会被垃圾回收了;

【问题】那作为GcRoot的对象就一直不会被回收吗?
【答案】
所以GC根对象被回收的时间点:栈里面的,线程执行完成,栈销毁了这GCroot对象就回收了;静态变量做GCroot的时候,只有fullgc的时候方法区被gc,这个时候才会被回收,为什么静态变量设计是类中独一份的,存活周期很长

引用传递和值传递相关的

165564292306022

【核心】
引用传递子方法赋值=
图中,标为绿色的地方,这里是v=val是重新指向一块引用地址,前面的value v 的地址块无关

堆中有线程私有的分配缓冲区,这部分是用来装什么的?其他线程可以访问吗?

【核心】
有,你可以理解这部分就是放一些对象,装自己线程独有的对象,其它线程不可以访问的

你理解这个概念就行

就是对象是在eden中,但是是我自己线程私有的一些对象

【完整解释】
这其实是JVM的优化,知道相应的概念即可
本地线程分配缓冲——TLAB
TLAB是虚拟机在堆内存的划分出来的一块专用空间,是线程专属的。在TLAB启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

ps:这里说线程独享的堆内存,只是在“内存分配”这个动作上是线程独享的,至于在读取、垃圾回收等动作上都是线程共享的。即是指其他线程可以在这个区域读取、操作数据,但是无法在这个区域中分配内存。

TLAB 生命周期
在分代收集的垃圾回收器中,TLAB是在eden区分配的。TLAB 是从堆上 Eden 区的分配的一块线程本地私有内存。线程初始化的时候,如果 JVM 启用了 TLAB(默认是启用的, 可以通过 -XX:-UseTLAB 关闭),则会创建并初始化 TLAB。同时,在 GC 扫描对象发生之后,线程第一次尝试分配对象的时候,也会创建并初始化 TLAB。
在 TLAB 已经满了或者接近于满了的时候,TLAB 可能会被释放回 Eden。GC 扫描对象发生时,TLAB 会被释放回 Eden。TLAB 的生命周期期望只存在于一个 GC 扫描周期内。在 JVM 中,一个 GC 扫描周期,就是一个epoch。那么,可以知道,TLAB 内分配内存一定是线性分配的。

String.inern在JDK1.7前后的变化

String.intern在jdk1.7之后就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。
如下图:

165564377308537

jdk7之后如下图:

165564424897611

posted @ 2022-07-12 19:54  Faetbwac  阅读(31)  评论(0编辑  收藏  举报