深入浅出JVM

知识点汇总

一、JVM内存模型

栈:也叫方法栈,存储局部变量表,动态链接,方法出口等信息,调用方法执行入栈,方法返回执行出栈。

本地方法栈:与方法栈类似,区别是执行native方法使用的是本地方法栈,执行java方法使用的是方法栈。

(注:native方法即使用native关键字修饰的java方法,目的在于与操作系统进行交互,调用操作系统语言程序。)

程序计数器:保存当前线程所保存的字节码位置,每个线程工作时都有一个独立的计数器,程序计数器只为java方法服务,执行native方法时,程序计数器为空。

堆:jvm内存管理中最大的一块,用于存放对象的实例,当对空间不足时,会抛出OOM异常(java.lang.OutOfMemoryError),jvm把堆内存进行分代管理,由垃圾回收器进行对象的回收管理。

方法区:又叫非堆区,用于存储已被虚拟机加载的类信息、常亮、静态变量、编译器优化后的代码等数据,jdk1.7的永久代和1.8的materspace都是方法区的一种实现。

栈、本地方法栈、程序计数器是线程独占的。

堆、方法区是线程共享的。

二、Java内存模型(JMM)

JMM需要保证原子性、可见性、有序性,通过以下四种方式:

1、基本数据类型读或写(long、double除外):是原子性的。

2、synchronized:通过java的两个高级字节码指令monitorenter和monitorexit来保证原子性,

3、volatile:

可见性保证:强制变量的赋值会刷新回主内存,强制变量的读取会从主内存中重新加载,保证不同的线程总是能看到该变量的最新值。
有序性保证:通过指令重排序保证变量读写的有序性。
4、happens-bofore原则:

程序顺序原则:一个线程内必须保证语义串行性;
锁规则:同一把锁的解锁必须发生在再次枷锁之前;
传递性规则、线程的启动、中断、终止规则等。

三、类的加载与卸载

深绿色表示加载过程,浅绿色表示生命周期。

加载:通过类的完全限定名找到字节码文件,通过字节码文件创建class对象。

验证:图中始终验证方法。

准备:为static修饰的变量分配内存,初始值0或者null。(final不分配,因为在编译时已经分配)

解析:图中。

初始化:看图中解释,若类的父类没有初始化,则先初始化父类的静态块和静态变量,只有对类的主动使用时才会初始化。

初始化的出发条件:创建类实例、访问类静态变量或者静态方法、class.forName()发射加载、某个子类被初始化。
使用:实例化。

卸载:java前三种类加载器的加载的类不会被卸载,用户自定义类加载器加载的类才会被卸载。

四、类加载器与加载模式

类加载器:启动类加载器、扩展类加载器、应用/系统加载器、用户自定义加载器。

双亲委派模式好处:

避免类的重复加载;
防止java系统类被篡改。

五、内存分代回收

年轻代:大部分对象会在Eden区生成,Eden区满时,会在s1和s2中交替保存,达到一定次数对象会晋升到老年代。

老年代:存储由年轻代晋升到老年代存储时间较长的内存对象。

永久代:主要保存类信息等内容。(这里只是一种划分方式,并不是特指1.7 PermGen/1.8 Metaspace)

六、垃圾回收算法判断算法:

引用计数法
可达性分析算法
清除算法:

引用计数算法:通过对象被引用的次数确定对象是否被使用,缺点是无法解决循环引用的问题。
复制算法:分为from块和to块,开始在from块,回收时将from块存活的对象复制到to块,将from块清空,to块变from块,from块变to块,缺点是内存使用率较低。
标记清除算法:分为标记对象和标记不在使用的对象两个阶段,缺点是会产生内存碎片。
标记整理算法:与标记清除算法相同,不过在清楚后会进行内存整理。
分代回收算法:当前的商业虚拟机的垃圾收集都是采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把堆划分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收
jvm提供的年轻代回收算法属于复制算法,CMS、G1,ZGC属于标记清除算法。

6.1 CMS算法

CMS是标记清除算法,JDK1.7之前的默认垃圾回收算法,并发收集,停顿小。

初始标记(STW):标记GC roots直接可达的对象。
并发标记:GC线程应用线程并发进行,主要标记可达的对象。
重新标记(STW):停顿时间比并发标记小很多,比初始标记稍长,主要是从新扫描并标记。
并发清理:并发执行,开始清理未标记的对象。
并发重置:为下一次GC充值相关数据。

6.2 G1算法

G1算法JDK1.9之后默认回收算法,特点是保持高回收率的同时,减少停顿。

年轻代回收:采用复制算法。
老年代回收:采用标记清除算法,同时也会回收年轻代。
G1可以通过设置JVM参数设置rejion(区)的大小,范围是1~32M,还可以设置期望的最大停顿时间。

6.3 ZGC算法

ZGC针对大内存的回收,可以控制在10ms以内的停顿。

着色指针
读屏障
并发处理
基于Rejion
内存压缩(整理)
ZGC GC过程:

面试考察点与真题

1、JVM内存模型、JMM内存模型

2、类加载过程、双亲委派机制

3、原子性、可见性、有序性的保证机制

4、G1适合对最大延迟有要求的场合,ZGC适合64位对大内存有要求的场合

5、垃圾回收的并发数,偏向锁的设置

 

1、栈上分配释放压力,如何编写适合内联优化的代码

2、经常fullGC的问题,内存泄漏问题

3、

4、

 

1、见上

2、年轻代晋升、老年代空间不足、永久代空间不足

3、见上

4、见上

5、见上

6、见上

 

7、强制主内存读写同步,防止指令重排序

10、

强引用:不会被GC回收

弱引用:每次GC都会被回收

虚引用:必须和引用兑现联合使用,跟总一个对象被垃圾回收的过程

软引用:空间不足会被GC回收

强引用:

只要引用存在,垃圾回收器永远不会回收

Object obj = new Object();

可直接通过obj取得对应的对象 如obj.equels(new Object());
而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。

软引用:

非必须引用,内存溢出之前进行回收,可以通过以下代码实现:

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();(通过get获取这个对象,由于obj指向了null,回收的时候会回收这个obj对象,但是如果还没有来得及回收,可以取得这个对象,如果已经回收了会返回null)

这时候sf是对obj对象的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用于实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;

当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

弱引用:

第二次垃圾回收时回收,可以通过如下代码实现

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用:

垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除

虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用
虚引用主要用于检测对象是否已经从内存中删除

11、

JMC:飞行计数器

MAT:堆分析工具

JStack:线程分析工具

posted @ 2019-11-05 20:08  别动我的猫  阅读(424)  评论(0编辑  收藏  举报