jvm interview related (don't spray if you don't like it)

Eight basic data types

  • character type

  • char

  • number type

  • int

  • float

  • long

  • short

  • double

  • byte

  • Boolean type

  • boolean

jvm memory model (runtime data area)

Execution logic: the class enters the method area|metaspace through the class loader. Of course, the relevant information or instructions of the class are placed in the metaspace. When the main method is executed or the thread is started, a thread stack|stack space will be created. Regardless of whether it is new or executing other instructions, it is handed over to the program counter, which notifies the execution engine, and the execution engine goes to the metaspace or method area to know which instruction it wants to execute and the data generated by executing this instruction. If it is a new object, it will be placed in the heap space. If it is some local variables, it will be stored in the thread stack. If it is a variable modified by the native method, it will be placed in the local method stack. But now the execution is implemented inside the stack frame. After we create the object, the reference address of the object will be stored in the local variable table, or the data for assignment will be stored in the local variable table, and the data will be pushed on the stack. Passed to the operand stack, the operand stack converts the symbolic reference of the code into some symbols of the current class in the metaspace through dynamic linking, during which it will frequently deal with the program counter, and finally returns to the main The location of the method.

1. Thread stack - thread private

Stack space, also called thread stack, because this space is exclusive to threads. The stored data is the stack frame of the method

Each thread of the JVM corresponds to a thread stack, and each method of a thread will allocate a stack memory space. The stack frame includes: local variable table, operand stack, dynamic link and method exit.

  • Local variable table: store eight basic data types (int, float, byte, etc.), if it is a reference data type, it stores its memory address in the heap, that is, a pointer to the object.

  • Operand stack: a temporary space to store operands during operand operation, and the data of local variables will not be deleted during operation.

  • Dynamic Linking: Converts symbolic references to code to direct references in the method area (runtime constant pool).

  • Method exit: stores the position where the method returns to the previous layer after the method in the stack frame is executed.

2. Local method area (local method stack) - thread private

The space for running native methods, that is, a piece of space when the native native method is running. Executing the native method native will generate some data, which is stored in this area

3. Program Counter - Thread Private

After the class is loaded, there will be a lot of codes. These codes are actually some instructions. These instructions are actually serial numbers. After decompiling the class, you will see some instructions. These instructions are all serial numbers. These serial numbers are Tell the execution engine through the program counter, the execution engine executes specific instructions through the cpu, puts these codes in the cpu register, and then executes the operation through the arithmetic unit. How does the execution engine know which line of code we want to execute? In fact, through the program counter, it will send the execution instruction code to the execution engine, then you will know how to execute it in order (you can view the bytecode file by adding .class file with javap -v)

4. Method area (metaspace) - shared by threads

When a class is loaded into the runtime data area, it is first loaded into the metaspace

It mainly includes: class information (for example, what methods are there, who is the parent class, and who is the parent class method), constants, static variables, runtime constant pool, and direct memory is operated.

5. Heap-thread sharing

What is stored is the new class object, but not all new objects are in the heap space. In a method, if the object is not exported, it will be destroyed after the method ends, and the object at this time is in the stack space. It is created when the JVM starts. The heap memory occupied by the object is managed by the automatic memory management system, that is, the garbage collector reclaims it.

  • See 4.jpg for the division of Survivor district

  • Young generation 1:3

  • eden 8:10 Store objects, if they are full, find garbage through the GCRoots reachability algorithm, collect and recycle garbage through minor GC, and copy other surviving objects to s0 or s1 through the copy algorithm

  • s0 1:10 Move s1 to s0 or move eden to s0

  • s1 1:10 Move s0 to s1 or move eden to s1

  • Old generation 2:3 If the age of the new generation reaches the set age, it will move to the old generation and fullGC will occur when the old generation is full

  • minor GC is also called youngGc: there will be STW, eden or s0 or s1 will be stuck when garbage collection is in progress, and the STW time is relatively short and negligible

  • Major GC: GC in the old age

  • GC root: local variables of the virtual machine stack, static variables, variables of the local method stack, etc.

  • full GC:当老年代满了以后,fullGc会去回收老年代和年轻代的垃圾,此时发生的stw会远远长于minor GC 反正数十倍大小 等同于majorGC+minorGC
    像eden对象移动到s1的时候采用的垃圾回收算法其实是复制算法

6.JVM中对象及常量、局部变量、全局变量的存储位置

1. 局部变量

基本数据类型:变量名和变量值存储在方法栈中。

引用数据类型:变量值存储在方法栈中(存储的是堆中对象的内存地址),所指向的对象是存储在堆内存中(如new出来的对象)。

2. 全局变量|普通成员变量

基本数据类型:变量名和变量值存储在堆内存中。

引用数据类型:变量名存储的是所引用对象的内存地址,变量名和变量值存储在堆内存中

3.常量

final static修饰的常量在方法区中的常量池

static修饰的变量在方法区中

对象逃逸分析

1.7之前,对象创建都是在堆空间进行,但是有个问题,方法中未被外部访问的对象

test1,这里面对象频繁在堆空间创建,方法出口后,需要对对象进行gc,浪费了性能

1.7以后,就会进行一次对象逃逸分析,于是没有被外部方法访问的对象就直接在栈上创建,随着方法的出栈而销毁,无须GC。

在栈上分配内存的时候,会把聚合量替换为标量,来减少栈空间的开销,也是为了防止栈 上没有足够连续的空间存放对象

  • 标量:Java中的八种基本数据类型

  • 聚合量:引用的数据类型

也就是说,在栈上分配内存的时候,会把引用数据类型如对象之类的拆分为八种基本数据类型。

逃逸的条件:

  • 能否逃出当前方法,也就是当前方法是否将对象返回出去

是不是所有的对象和数组都会在堆内存分配空间(仅限1.7以后jdk环境)

不一定,随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。但是这也并不是绝对的。在开启逃逸分析之后,也并不是所有对象都没有在堆上分配

如何判断一个对象是否是垃圾|垃圾定位

引用算法:相互引用

有个缺点:就会产生过多的垃圾,A引用B,B引用A,

可达性算法?

eden区满后,会触发GC root,将GC roots作为起点,从该节点开始向下搜索引用对象,找到的对象都标记为非垃圾对象,其余未标记到的对象就都标记为垃圾对象。

可以作为GC Roots的对象有以下几种:

  • 1.虚拟机栈中引用的对象

  • 2.方法区类静态属性引用的变量

  • 4.本地方法栈JNI引用的对象

垃圾回收算法?

1.标记清除算法(存活对象标记,未标记,循环挪到空间链表后进行删除,涉及移动,所以慢,适用存活对象多的)

标记清除算法是先找到内存里的存活对象并对其进行标记,然后统一把未标记的对象统一的清理。

缺点:位置不连续 产生空间碎片

特点: 简单、收集速度快,但会有空间碎片,空间碎片会导致后面的GC频率增加。

适合场景:存活对象多,需要回收的少,适用于老年代的垃圾回收,因为老年代(老年代存活对象多,垃圾对象比较少)一般存活对象会比回收对象要多。

废话:会扫描所有对象,将那些挂未被标记的进行一次遍历,这期间会把未被标记的对象加入到空闲的空间链表中作为空闲的块,而且这个阶段对堆对象越多的耗费的时间久越长,也就证明了未被标记的对象越多,耗费的时间越长,性能就会越差

2.复制算法(存活对象复制到另一块内存,然后清空当前内存,存活少的情况,存活越少复制就越快,所以适用新生代)

按照内存容量将内存划分为相等大小的两块内存区域。每次使用时只使用其中的一块,当这一块内存满后将存活的对象复制到另一块内存区域上去,将剩余的已使用的内存回收。

这种算法实现简单,内存效率高,不易产生内存碎片,但是内存使用率被压缩到了以前的一半。当存活对象增多时,复制算法的效率会大大降低。

缺点:没有碎片但是浪费一半的空间

特点:收集速度快,可以避免空间碎片,但是有空间浪费,存活对象较多的情况下复制对象的过程等会非常耗时,而且需要担保机制。

适合场景: 只有少量对象存活的场景,这也正是新生代对象的特点,所以一般新生代(存活少,绝大多数挂掉的对象被回收)的垃圾回收器基本都会选择标记复制法。

废话:从概念知道,将存活的对象都移到另一块内存,剩下的对象都被回收掉,也就是存活对象少的话,移动速度就快,这块就比较使用eden|s0|s1区了

3.标记整理算法

结合以上两个算法,为了避免缺陷而提出,标记阶段和标记清除算法相同,但标记后不是清理对象,而是将存活的对向移至内存的一端,然后清除未标记的另外一块内存的对象,将可回收的空间回收后将存活的和未使用的空间全部整理

缺点:没有碎片,效率偏低(主要是要对存活和未存活的对象进行挪动,虽然不会造成空间浪费,但是牺牲了性能)

特点: 相对于标记复制法不会浪费内存空间,相对标记清除法则可以避免空间碎片,但是速度比其他两个算法慢(主要是又要标记,又要将没被标记挪到其他内存块,所以慢)。

适合场景: 内存吃紧,又要避免空间碎片的场景,老年代想要避免空间碎片问题的话通常会使用标记整理法。

常用垃圾收集器?

1.8及其之前的版本

年轻代 (涉及到复制算法的都为年轻代)

  • serial(老版本前收集器)(适用于单核服务器|小型应用): 串行收集的,采用复制算法,不仅只用一条线程收集,而且在收集的同时所有用户线程必须停止(stop the world)

  • ParNew(适用于多核服务器):searial的多线程版本,并行收集的,与searial无区别,单核情况下性能会比searial低,所以默认开启的收集线程和cpu数量一致,收集是会开启多个线程进行收集,同时所有用户线程进入等待状态 和cms老年代收集器搭配使用

  • parallelScavenge(1.8默认收集器)(适用于多核服务器,并且注重吞吐量,高效利用cpu资源): 并行的收集器,和parnew最大的区别就是parnew的目标尽可能缩短用户线程等待时间,parallelScavenge的话是将整块达到一个可以控制的吞吐量(吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间))

老年代 (复制算法除外都在老年代)

  • Serial old(老版本前收集器):是searial老年代版本,同样也是一个单线程的收集器,采用标记整理算法,与searial搭配使用

  • CMS :采用标记清除算法,以最短回收停顿时间为目标,整个过程耗时最长的并发标记、并发清除都是和用户线程一起工作的,总的来说就是并行执行。整个过程会造成有些垃圾无法处理,主要是在清理阶段的时候,用户线程会产生垃圾,而且该收集器还不能和其他收集器一样等老年代填满了才收

  • 初始标记:标记活跃对象,速度快

  • 并发标记:标记全部垃圾对象,耗时长

  • 重新标记:修改并发标记记录,让用户线程继续运行时,导致变化的对象的标记记录,耗时短

  • 并发清除:采用标记-清除算法清除垃圾对象,耗时较长

  • Parallel Old(1.8默认收集器):和parallel Scavenge一起使用,是这个的老年代版本,多线程的收集器,采用标记整理算法,和parallel scavenge使用可以有效发挥多核cpu的计算能力

G1 1.9默认的垃圾收集器(适用新生代和老年代)

采用的标记整理算法,不会产生内存碎片,但是会产生stop the world

  • 初始标记

  • 使用一个垃圾回收线程进行初始标记,此刻其他线程暂停,主要标记的是什么样的对象垃圾:标记GCroot能够直接关联到的对象

  • 并发标记

  • 从GCRoot触发可达性分析存活对象,和用户代码进行并行执行,

  • 重新标记

  • 重新标记,也是记录用户线程在运行过程中,产生的垃圾对象,该过程是多个minorGCthread执行的

  • 初始清除

  • 回收掉新生代区域垃圾对象

  • 并发清除

  • 清理掉老年代区域产生的垃圾对象

G1和cms的区别

  • G1适用于新生代和老年代,cms仅适用于老年代

  • CMS主要采用的是分代模型如新生代老年代,G1是采用分区模型,将堆分为2048个区,每个区域都可以作为新生代和老年代适用并且大小可以根据期望暂停的时间动态进行调整

  • G1主要流程为初始标记、并发标记、重新标记、初始清除、并发清除,CMS主要是初始标记、并发标记、再次标记、并发清除,cms采用了标记清除算法,会产生很多内存碎片,G1采用的标记整理算法,不会产生内存碎片,但是会出现stw,通过初始清理、并发清理对垃圾进行回收,最后重新分配区域比例

  • G1堆特别大的应用,cms适用于堆不大不小的

评价垃圾回收算法好坏的标准?

  • 吞吐量(用户运行代码时间/(用户运行代码时间+垃圾回收时间)) 越高越好

  • 停顿时间 越低越好

题外话,一些更厉害的收集器

  • ZGC 11默认垃圾收集器

  • shenandoah G1增强版 redhat开发

  • Epsilon 测试 不会给你真正的垃圾手机

垃圾收集器组合的优缺点

  • 串行收集器->serial和serial old

  • 只能有一个垃圾回收线程执行,用户线程暂停。适用于内存比较小的嵌入式设备

  • 并行收集器(吞吐量优先)->parallel scavenge、parallel old

  • 多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态,适用于科学计算,后台处理等着交互场景

  • 并发收集器(停顿时间有限)->CMS,G1

  • 用户线程和垃圾线程同时执行,垃圾收集线程执行的时候不会停顿用户线程运行。适用于相对时间有要求的场景,比如web

如何搞定jvm内存溢出?

  • 发生的原因?
    老年代满了但是Gcroots通过可达性算法分析一直回收不掉 此刻就会发生oom

  • 解决方案:加大jvm内存 加大年轻代的比例,提高对象的分代年龄,提高s区比例,避免代码内存泄漏

  • top 查看所有进程,top -hp 查看当前进程下的所有线程,jstack -grep

  • 观察 如果是业务逻辑 检查业务逻辑 ,如果是系统线程就查询为什么频繁GC 最后再加上GC日志的处理

  • 减少GC频率---G1

  • 适当增加对内存的空间

  • 合理设置G1垃圾收集器的停顿次数

  • 垃圾回收的临界线

  • -xx:InitalingHeapOccupanPercent = 50

  • 增加垃圾回收线程数量

  • -xx:ConcGCThreads = 10

内存泄漏:内存泄漏其实就是空间无法回收最后就会造成oom outOfMemoryError

jvm调优 调的是什么

  • oom

  • jvm内存预分配(新生代和老年代,eden区和s0 s1区)

  • 使用的是那一套垃圾回收处理机制

  • 频繁stw情况

jvm参数

堆栈内存相关

  • -Xms 设置初始堆的大小

  • -Xmx 设置最大堆的大小

  • -Xmn 设置年轻代大小,相当于同时配置-XX:NewSize和-XX:MaxNewSize为一样的值

  • -Xss 每个线程的堆栈大小

  • -XX:NewSize 设置年轻代大小(for 1.3/1.4)

  • -XX:MaxNewSize 年轻代最大值(for 1.3/1.4)

  • -XX:NewRatio 年轻代与年老代的比值(除去持久代)

  • -XX:SurvivorRatio Eden区与Survivor区的的比值

  • -XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。

  • -XX:MaxTenuringThreshold设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代

垃圾收集器相关

  • -XX:+UseParallelGC:选择垃圾收集器为并行收集器。

  • -XX:ParallelGCThreads=20:配置并行收集器的线程数

  • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。

  • -XX:CMSFullGCsBeforeCompaction=5 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行5次GC以后对内存空间进行压缩、整理。

  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

辅助信息相关

  • -XX:+PrintGCDetails 打印GC详细信息

  • -XX:+HeapDumpOnOutOfMemoryError 让JVM在发生内存溢出的时候自动生成内存快照,排查问题用

  • -XX:+DisableExplicitGC 禁止系统`System.gc()``,防止手动误触发FGC造成问题.

  • -XX:+PrintTLAB 查看TLAB空间的使用情况

jvm体系结构?

  • jvm指令集

  • 类加载器

  • 执行引擎-也相当于是jvm的cpu

  • 内存区-jvm的存储单元

  • 本地方法调用-调用c、c++实现的本地方法

引用类型?

无论是可达性分析法还是引用计数法,垃圾回收都与引用类型有关

  • 强引用:被引用的对象不会被回收 常见 new

Object obj = new Object();

  • 弱引用:weakRefrence,被关联的对象在下次垃圾收集之前被回收

Obejct oj = new Object();

WeakReference<Obejct> wf = new WeakReference<Obejct>(oj);

oj = null; System.gc(); //下面会发现有时候直接返回null;wf.get();

  • 软引用:softRefrence,被关联的对象内存不足时会被回收

SoftReference<String> ref = new SoftReference<String>("hong");

  • 虚引用:这个引用也有人叫幻引用,也很明显,引用一个不存在,随时会被干掉,算是所有引用中最容易被干掉的。也就是随时可能被回收

// 虚引用

Object oj= new Object();

ReferenceQueue req= new ReferenceQueue();

PhantomReference<Object> pr= new PhantomReference<Object>(oj, req);

// 每次返回Null

System.out.println(pr.get());

//返回是否被删除

System.out.println(pr.isEnqueued());

Guess you like

Origin blog.csdn.net/qq_31671187/article/details/128632661