https://www.sohu.com/a/244927394_465221

1.谈谈你对解析与分派的认识。

2.触发fullGC的情况
1.System.gc()方法的调用;
2.老年代空间不足;
3.永生区空间不足;
4.CMS GC时出现promotion failed和concurrent mode failure
5.统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
6.堆中分配很大的对象
https://blog.csdn.net/chenleixing/article/details/46706039/

3.Java类加载器包括⼏种?它们之间的⽗⼦关系是怎么样的?双亲委派机制是什么意思?有什么好处?
类加载器:引导类加载器<-拓展加载器<-系统加载器<-自定义加载器
引导类加载器:bootstrap ClassLoader是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,即将类库加载到虚拟机内存中,用来加载java的核心库,此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用;

  bootstrap ClassLoader使用的是c/c++语言实现的,嵌套在JVM内部;
双亲委派机制就是如果一个类加载器要进行类加载,它先不自己加载,而是向父加载器一层层委托,如果父加载器加载不了,则自己加载
好处:可以避免类的重复加载,如果父类加载器已经加载过该类,则不用在加载了
https://www.sohu.com/a/244927394_465221

4.如何⾃定义⼀个类加载器?你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗?
自定义类:所有继承java.lang.ClassLoader都归为自定义类加载器加载器
自定义类加载的意义:

加载特定路径的class文件
加载一个加密的网络class文件
热部署加载class文件

5.堆内存设置的参数是什么?
-Xmx 设置堆的最大空间大小
-Xms 设置堆的最小空间大小

6.Perm Space中保存什么数据?会引起OutOfMemory吗?
全称是Permanent Generation space,是指内存的永久保存区域.
用于存放,Class和常量池等的信息,Class在被 Load的时候被放入PermGen space区域.所以当我们的应用服务器在启动的时候会加载很多的CLASS的时候会出现OutOfMemoryError: PermGen spac的错误. 解决方法自然也就是要加大应用服务器在jvm启动时候所需要初始化的perGen space的size 了,一般是在应用服务器的启动参数的jvm配置中加入 :
-XX:MaxNewSize=256m -XX:MaxPermSize=256m
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。 如果你的WEB APP下都用了大量的第三方jar, 其大小 超过了jvm默认的大小(4M)那么就会产生此错误信息了。
解决方法: 手动设置MaxPermSize大小
改正方法:-Xms256m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m

7.java的四种引用;java的GC算法
强引用、软引用、弱引用、虚引用
1、标记 -清除算法(Mark-Sweep)
“标记-清除”算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。
它的主要缺点有两个:
(1)效率问题:标记和清除过程的效率都不高;
(2)空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,碎片过多会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World。
2、复制算法(Copying)
为解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
它的主要缺点有两个:
(1)效率问题:在对象存活率较高时,复制操作次数多,效率降低;
(2)空间问题:內存缩小了一半;需要額外空间做分配担保(老年代)

From Survivor, To Survivor使用的就是复制算法。老年代不使用这种算法,

3、标记-整理(Mark-Compact)
复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
4、分代收集算法(Generational Collection)
GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。
“分代收集”算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

8.你有没有遇到过OutOfMemory问题?你是怎么来处理这个问题的?处理 过程中有哪些收获?
permgen space、heap space 错误。
常见的原因
内存加载的数据量太大:一次性从数据库取太多数据;
集合类中有对对象的引用,使用后未清空,GC不能进行回收;
代码中存在循环产生过多的重复对象;
启动参数堆内存值小。

9.StackOverflow异常有没有遇到过?⼀般你猜测会在什么情况下被触发?如何指定⼀个线程的堆栈⼤⼩?⼀般你们写多少?
大家都知道,Windows程序的内存机制大概是这样的,全局变量(局部的静态变量本质也属于此范围)存储于堆内存,该段内存较大,一般不会溢出;函数地址、函数参数、局部变量等信息存储于栈内存,VC6中栈内存默认大小为1M,对于当前日益扩大的程序规模而言,稍有不慎就可能出问题。(动态申请的内存即new出来的内存不在栈中)即如果函数这样写:voidtest_stack_overflow(){charchdata=new[210241024];delete[]chdata;}是不会出现这个错误的,而这样写则不行:voidtest_stack_overflow(){charchdata[21024*1024];}大多数情况下都会出现内存溢出的错误,不信在vc6中随便做个程序,调用一下这个函数试式。出现栈内存溢出的常见原因有2个:1>函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈。2>局部静态变量体积太大第一种情况不太常见,因为很多情况下我们都用其他方法来代替递归调用(反正我是这么做的),所以只要不出现无限制的调用都应该是没有问题的,起码深度几十层我想是没问题的,这个我没试过但我想没有谁会把调用深度作那么多。检查是否是此原因的方法为,在引起溢出的那个函数处设一个断点,然后执行程序使其停在断点处,然后按下快捷键Alt+7调出callstack窗口,在窗口中可以看到函数调用的层次关系。第二种情况比较常见了,我就是犯了这个错误,我在函数里定义了一个局部变量,是一个类对象,该类中有一个大数组,大概是1.5M。解决办法大致说来也有两种:1>增加栈内存的数目2>使用堆内存增加栈内存方法如下,在vc6种依次选择Project->Setting->Link,在Category中选择output,在Reserve中输入16进制的栈内存大小如:0x10000000,然后点ok就可以了。

10.内存模型以及分区,需要详细到每个区放什么。
Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念。java内存模型中分为主内存和工作内存。主内存里面存储着所有变量,主内存是共享内存区域,所有线程都可以访问。每一个线程都私有一个工作内存,工作内存里面保存着主内存里面变量值的副本,线程对变量的操作都是在工作内存中完成,操作结束后再放回主内存。主内存可粗略认为是堆,工作内存认为是栈。操作系统中,一般CPU都会从内存取数据到寄存器,然后进行处理,但由于内存的处理速度远远低于CPU,导致CPU在处理指令时往往花费很多时间在等待内存做准备工作,于是在寄存器和主内存间添加了CPU缓存,CPU缓存比较小,但访问速度比主内存快得多。Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间。
栈区:

栈分为java虚拟机栈和本地方法栈

1) 重点是Java虚拟机栈,它是线程私有的,生命周期与线程相同。

2) 每个方法执行都会创建一个栈帧,用于存放局部变量表,操作栈,动态链接,方法出口等。每个方法从被调用,直到被执行完。对应着一个栈帧在虚拟机中从入栈到出栈的过程。

3) 通常说的栈就是指局部变量表部分,存放编译期间可知的8种基本数据类型,及对象引用和指令地址。局部变量表是在编译期间完成分配,当进入一个方法时,这个栈中的局部变量分配内存大小是确定的。

4) 会有两种异常StackOverFlowError和 OutOfMemoneyError。当线程请求栈深度大于虚拟机所允许的深度就会抛出StackOverFlowError错误;虚拟机栈动态扩展,当扩展无法申请到足够的内存空间时候,抛出OutOfMemoneyError。

5) 本地方法栈 为虚拟机使用到本地方法服务(native)

堆区:

1) 堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例。

2) 堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区,放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。

3) 会有异常OutOfMemoneyError

方法区:

1) 被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanmentgeneration)

2) 垃圾回收很少光顾这个区域,不过也是需要回收的,主要针对常量池回收,类型卸载。

3) 常量池具有一定的动态性,里面可以存放编译期生成的常量;运行期间的常量也可以添加进入常量池中,比如string的intern()方法。

程序计数器:

1) 当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。

2) Java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都需要一个独立的程序计数器,所以它是线程私有的。

3) 唯一一块Java虚拟机没有规定任何OutofMemoryError的区块
https://blog.csdn.net/qiuchaoxi/article/details/79889097

11.分派:静态分派与动态分派。
https://www.cnblogs.com/mengchunchen/p/7860397.html

12.虚拟机在运行时有哪些优化策略
公共子表达式消除、数字范围检查消除、方法内联、逃逸分析
https://www.cnblogs.com/kubidemanong/p/9457004.html

13.请解释StackOverflowError和OutOfMemeryError的区别?
1、StackOverflowError
源代码解释说:抛出这个错误是因为递归太深.其实真正的原因是因为Java线程操作是基于栈的,当调用方法内部方法也就是进行一次递归的时候就会把当前方法压入栈直到方法内部的方法执行完全之后,就会返回上一个方法,也就是出栈操作执行上一个方法。
2、OutOfMemoryError
源代码解释说:因为内存溢出,JVM不能分配给对象的创建空间.并且GC也不能够回收足够的空间.当你创建对象的速度快于JVM回收空间的时候就会发生空间不足这个问题:
只要我们知道错误发生的原因了。当出现相就的问题就可以快速定位问题,迅速解决问题。

StackOverflowError:递归过深,递归没有出口。
OutOfMemoryError:JVM空间溢出,创建对象速度高于GC回收速度。

14.在JVM中,如何判断一个对象是否死亡?
https://blog.csdn.net/wuzhenwei0419/article/details/85261255
引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。
这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。

可达性分析算法:在主流的商用程序语言(Java , C# 等)的主流实现中都是使用可达性分析(Reachability Analysis)来判定对象是否是存活的。

这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连的话,则证明此对象是不可用的。
在Java 语言中,可作为GC Roots的对象包括下面几种:
    a.虚拟机栈(栈帧中的本地变量表)中引用的对象
    b.方法区中类静态属性引用的对象
    c.方法区中常量引用的对象
    d.本地方法栈中JIT(即一般说的Native方法)引用的对象

即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize方法。当对象没有覆盖finalize方法,或finalize方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

15.类加载过程
加载:先根据一个类的全限定名来获取此类的二进制流(可一从class文件、网络、数据库),然后将这个二进制流的静态存储结构转化为方法区的运行时数据结构;然后在内存中生成一个。class对象,用来作为该类各种数据的访问入口。
验证:验证字节流是否符合当前虚拟机的要求
准备:开始给类静态变量等分配内存或赋值
解析:虚拟机将常量池以内的符号引用替换为直接引用
初始化:初始化阶段是执行类构造器<clinit>()方法的过程.
使用:
卸载:</clinit>