JVM知识点总结

JVM的作用

垃圾回收,分区管理,类加载,跨平台。

java的内存区域:

主要有两个版本:

JVM知识点总结

JVM知识点总结

每块内存区域的作用:

一、程序计数器

有两个作用(1)字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常、线程恢复都要依赖拍这个计数器来完成。(2)java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个时刻,一个处理器都只会执行一条线程中的指令。因此在多线程的情况下,线程为了切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器。

二、java虚拟机栈

java虚拟机栈描述的是java方法执行的内存模型,每次方法调用时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口信息等。每一个方法调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

java虚拟机栈中有两种异常状况:栈溢出和内存溢出

三、本地方法栈

本地方法栈与虚拟机栈发挥的作用相似,它们的区别是虚拟机栈为虚拟机执行方法(也就是字节码)服务,本地方法栈为虚拟机使用到的Native方法服务。也会抛出栈溢出和内存溢出异常。

四、java堆

java堆是java虚拟机所管理的内存中最大的一块。所有的对象实例和数组要在这里分配。java堆 是垃圾收集器管理的主要区域

又被称为GC堆。

从内存回收的角度来看,现在的收集器基本采用分代收集算法,java堆中可以细分为:新生代和老年代。其中新生代又可以进一步分为eden,from survivor ,to survivor 。

JVM知识点总结

新生代与老年代的默认比例是1:2。新生代的默认比例是8:1:1。

内存分配策略:

1、大多数情况下,对象优先在新生代的Eden区中分配,当eden区中没有足够的空间进行分配时,虚拟机触发一次Minor GC。

2、如果创建的是大对象即需要大量连续内存空间的java对象(如很长的字符串或者数组)直接进入老年代。

3、对象在eden出生并经过第一次Minor GC后仍然存活,并且能被survivor容纳的话,将被移动到survivor空间中,并且对象年龄设为1,对象在survivor区中每熬过一次Minor GC ,年龄就增加1,当它的年龄达到默认值15时,就晋升到老年代中。

4、动态对象年龄判定,如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到默认值15

5,空间分配担保

在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间时否大于新生代所有对象总空间,如果这个条件成立,Minor GC可以确保安全。若不成立,查看HandlePromotionFailure 设置值是否允许担保失败,若允许,继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,若大于,尝试进行一次Minor GC(有风险),若小于或者参数设置不允许,改为进行一次Full GC。

从内存分配的角度看,线程共享的java堆中划分出多个线程私有的分配缓冲区(TLAB)。划分的目的,为了更好地回收内存和分配内存。

五、方法区(jdk1.8之后移除,如上图所示,取而代之的是元空间,元空间使用的是直接内存)

被移除的原因之一,方法区受到JVM本身设置固定栈大小上线,无法进行调整,元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到OOM的提示。

它用于存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。

六、运行时常量池(jdk1.7之后,已经将常量池从方法区中移除,在java堆中存放)

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量(文本字符串,被声明为final的常量值,基本数据类型的值)和符号引用(类和结构的完全限定名,字段名称和描述符,方法名称和描述符)。

七、直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但是这部分内存也被频繁使用。

在JDK1.4之后新加入了NIO类,引入了一种基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作,这样在某些场景中可以显著提高性能,因为避免了在java堆和Native堆中来回复制数据。

直接内存的分配不会受到java堆大小的研制,但是,既然是内存,肯定会受到本机总内存大小以及处理器殉职空间的限制。

 

对象的创建过程

JVM知识点总结

1,类加载检查(遇到一条new指令后,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有必须限制性相应的类加载过程。)

2,为新生对象分配内存,内存分配方式有指针碰撞和空闲列表两种方法,选择哪种方式由堆是否规整决定,而java堆是否规整又由所采用的垃圾回收器是否带有压缩功能决定。

JVM知识点总结

    垃圾收集器相关知识

判断对象已死:java虚拟机中采用可达性分析算法 。算法思路:通过一系列的称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

JVM知识点总结

可作为GC Roots 的对象包括下面几种:

虚拟机栈中引用的对象;本地方法栈JNI引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象。

JDK1.2之后,java对引用的概念进行了扩充,分为强引用,软引用,弱引用,虚引用。

垃圾收集算法有

标记-清除算法。 存在效率和空间问题。——》复制算法。解决了效率问题。现代的商业虚拟机都采用这种收集算法来回收新生代。如在新生代回收中,将eden和survivor中还存活着的对象一次性地复制到另外一块survivor空间上,然后清理掉eden和刚才用过地survivor空间。——》标记-整理算法。一般应用在老年代中。——》分代收集算法。根据各个年代的特点采用最适当的收集算法,在新生代中给,每次垃圾收集时都发现有大批量对象死去,只有少量存活,那就选用给复制算法,只需要付出少量存活对象的复制成本就可以完成手机。老年代中,因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。

标记-清除图示

JVM知识点总结

复制图示:

JVM知识点总结

标记-整理图示:

JVM知识点总结

垃圾收集器有以下几种

(1)Serial  单线程 新生代  复制算法 可与(4),(6)一起使用

(2)ParNew  多线程 新生代  复制算法 可与(4),(7)一起使用

(3)Parallel Scavenge  多线程 新生代  复制算法 可与(5)一起使用

目标达到一个可控制的吞吐量,提高吞吐量可以高效率地利用CPU时间,尽快地完成程序的运算任务。适合后台运算多,交互少的任务。

(4)Serial Old  单线程 老年代 标志-整理算法 

(5)Parallel Old  多线程 老年代 标志-整理算法 

(6)CMS   多线程 老年代 标志-清除算法 

目标尽可能地缩短垃圾收集时用户线程地停顿时间,提高响应速度,适合用户交互多的任务,如互联网网站,B/S系统的服务端上。

缺点:对CPU资源敏感;无法处理浮动垃圾;有大量空间碎片的产生;

运行过程:初始标记;并发标记;重新标记;并发清除

(7)G1 多线程 能独立管理整个GC堆,采用 标记整理算法

一款面向服务器端应用的垃圾收集器,使命替代JDK1.5发布的CMS收集器

优点:并行并发;分代收集;空间整合;可预测的停顿

运行过程:初始标记;并发标记;最终标记;筛选回收

3、初始化零值

虚拟机将分配到的内存空间都初始化为零值(不包括对象头)

4、设置对象头

虚拟机堆对象进行必要的设置,如这个对象是哪个类的实例,对象的哈希码,对象的GC分代年龄信息

5、执行init方法

把对象按照程序员的意愿进行初始化

类加载的过程

加载——》连接——》初始化——》验证——》准备——》解析