JAVA虚拟机理解
Posted worryrock
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA虚拟机理解相关的知识,希望对你有一定的参考价值。
1、虚拟机运行时数据区域
1.1、运行时数据区
JAVA虚拟机在执行JAVA程序过程中,会把他所管理的内存划分为若干个数据区域。
JAVA虚拟机运行时数据区
1.2、程序计数器
程序计数器可以看做是, 程序被执行时,内部字节码对应行号的指示器。这块空间很小,是线程私有的,也就是每个线程都有自己对应的程序计数器。程序在执行时,字节码解释器会通过改变计数器的值,来选取下一条需要执行的字节码。代码中分支、循环、跳转、异常、线程恢复等基础功能的操作都是由这个计数器来完成。
JAVA虚拟机中的线程,并非一直在执行,而是会由CPU轮换分配进行处理,也就是说每个线程都在轮流等着CPU宠幸,而每次CPU只会宠幸一个线程一小会儿而已。那么线程在轮流等待CPU的时候,为了保证下次轮到自己能够正常的继续执行,就需要计数器帮忙记录当前代码执行的位置。而且为了保证每个线程相互没有影响,计数器会给每个线程建立一个属于自己的计数器。
如果线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。
1.3、虚拟机栈
和计数器一样,栈(Stack)也是线程私有的,而且生命周期与线程是相同的,说白了就是线程执行结束,和这个线程先关的栈也就清理完了。
栈的特点和队列很相似,队列QUEUE的特性先进先出(FIFI),还有一种队列是双端队列DEQUE,支持两端同时进出。
双端队列的一种操作是从队里顶部进入、队列顶部出,也就是后进先出(LIFO),这种模式就是栈的模式。
栈适用于管理程序运行的,程序每执行一个方法时,都会创建一个栈帧(StackFrame),这个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每个方法从调用到结束,就对应着一个栈栈在栈中的入栈出栈过程。所以说栈里面存放的栈帧。
栈帧中的局部变量表中,存放的是一些编译期可知的基本数据类型(boolean、byte、int、short等)、对象引用、returnAddress(指向了一条字节码指令的地址),其中的对象引用就是类似于 String s = new String();中的s,实际的String是在堆中。
1.3、堆
对是虚拟机中内存最大的、所有线程都能共享访问的一块区域,这里存放着所有的对象实例、数组。堆中的内存在物理上是不连续的,但是逻辑上连续。
堆通常被分为新生代、老年代、永久代(JDK1.8后成为元空间),
1.3.1新生代
新生代是一个对象刚刚被创建的地方,99%的对象在新生代创建后,随着使用完以后就能被回收掉,而逃逸掉的1%的对象会被转移到老年代中
新生代还会被细分为Eden伊甸园区、幸存区suvivor0和suvivor1区,这三块区域的默认比例是8:1:1,Eden中的内存占大部分,在垃圾回收不掉时会转移到suvivor中
如果一个对象被GC清理标记了15次还没有销毁,那么就会转移到老年代中去。这15次的的清理是指suvivor0和suvivor1之间的转移次数。
suvivor被分为From和To两块区域,这两个区域是相互交换的,其中有一块必然是空的。如果suvivor0存在数据,清理完以后会把剩余的数据转移给suvivor1
那么下次再清理时,如果suvivor1还有数据就会给suvivor0。所以判断那块是From那块是To,主要看内部是否存在数据,有数据就是From,没有数据就是To
1.3.2老年代
一个对象在新生代幸存了15次,说明引用一直存在无法被垃圾回收,那么就要被转移到老年代。这个块空间比新生代要更大,一般是新生代的一倍。
如果这个区域的内存,超过了初始堆内存或者接近最大堆内存分配空间时,就会触发FULL GC,FULLGC会造成STW现象(Stop the World)。
1.4、方法区
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
java虚拟机将方法区描述为堆的一个逻辑部分,别名叫做Non-Heap,在JDK1.7前也被称之为永久代,在1.8以上成为元空间
Class文件中存放类的版本、字段、方法、接口等信息,还有一个就是常量池,用于存放编译期间生成的各类字面量和符号引用。
运行时常量池是方法区的一部分,永久代(元空间)的内存溢出,与常量池的增长也相关。
1.5、本地方法栈
本地方法栈与虚拟机栈发挥的作用相似,他们之间的区别不过是虚拟机栈执行java方法,而本地方法栈是虚拟机使用Native方法服务。
本地方法就是操作系统提供的方法,一般来说都是C开发的本地方法。JAVA调用C的方法都是采用Native关键字类修饰的方法。
JAVA调用C采用JNI方式 Java Native Interface
2、类加载
类从被加载到虚拟机内存开始,到卸载出内存,他的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载7个阶段
其中验证、准备、解析被统一称为连接阶段。
类被虚拟机加载的过程,就是加载、验证、准备、解析、初始化5个阶段,也可以说是加载、连接、初始化的过程。
2.1加载
加载过程,虚拟机将做3件事情:1、通过一个类的全限定名,来获取定义此类的二进制字节流;2、将这个字节流所代表的的静态存储的结构,转化为方法区的运行时数据结构。3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
2.2连接
连接包括验证、准备、解析三个阶段。
连接是为了确保Class文件的字节流包含的信息,符合当前虚拟机的要求,不会危害虚拟机的整体运行。
文件格式验证,比如文件版本号,是否是当前虚拟机的版本,常量池中的常量是否不被支持。
元数据验证,比如类是否有父类,类是否继承了不被允许继承的父类(final修饰的)
字节码验证,比如int类型的数据,不会按照long类型加载到本地变量表
符号引入验证,比如符号引用中的类、字段、方法的访问性(private、protected、public、default)是否可被当前类访问。
2.3准备
准备阶段是正式为类变量分配内存,并设置类变量初始值的过程,这些变量所以使用的内存都将在方法区中进行分配。
1、此时的内存分配,仅包含类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时,随着对象一起分配在java堆中。
2、初始值,是说数据类型的零值,假设public static int value=123;那么value在准备阶段后的初始化值是0,而不是123。
2.4解析
解析过程,是虚拟机常量池的符号引用,替换为直接引用的过程。
在把java编译为class文件的时候,虚拟机并不知道所引用的地址;而是采用助记符,也就是符号引用,通过转为真正的直接引用,才能找到对应的直接地址。
2.5初始化
public class Test{ public static int a = 1; } // 1、加载 编译文件为 .class 文件,通过类加载,加载到JVM // 2、连接 验证(1) 保证Class类文件没有问题 准备(2) 给int类型分配内存空间,a = 0; 解析(3) 符号引用转换为直接引用 // 3、初始化 经过这个阶段的解析,把1 赋值给 变量 a;
3、类加载器
类加载器分为虚拟机提供的类加载器和用户自定义的类加载器
1、java虚拟机自带的加载器
-
BootStrap 根加载器 (加载系统的包,JDK 核心库中的类 rt.jar)
-
Ext 扩展类加载器 (加载一些扩展jar包中的类)
-
Sys/App 系统(应用类)加载器 (我们自己编写的类)
2、用户自己定义的加载器
-
ClassLoader,只需要继承这个抽象类即可,自定义自己的类加载器
虚拟机在加载类的时候,采用双亲委派加载方式,也就是说加载的类,都让父类去加载,如果父类加载了就不进行加载,如果父类不负责加载这个类就再交给子加载器进行加载。
一层一层的让父类去加载,如果顶层的加载器不能加载,然后再向下类推。
boot根记载器加载的是jre运行环境最底层的包rt.jar;Ext扩展加载器,只加载ext扩展包下面的jar;sys系统加载器才会加载应用系统开发的类。
4、GC垃圾回收
4.1垃圾回收算法
1、引用计数法
计数器维护麻烦,对于循环引用的方式无法处理,会造成内存无法回收
4.2复制算法
复制算法,就是将一个区的全部无法回收的对象,全部复制到另一个区域中,然后再清空原来的这片空间。
优点是速度快,没有碎片空间,但是缺点是浪费内存,需要两块等同的空间。
4.3标记清除算法
通过2次扫描进行清理
扫描第一次,就会对活着的对象进行标记
扫描第二次,回收没有被标记的对象
优点是不需要额外的空间,但缺点时两次扫描,耗时较为严重,产生内存碎片,不连续
4.4标记整理算法
标记整理算法是在标记清除算法的基础上,再次进行扫描,将活着的对象在向左移动。
缺点是耗时非常严重。老年代采用这类算法,不经常发生GC,那么可以考虑这个算法
5、GC-ROOT
垃圾回收时,是通过GC-ROOT可达性分析来判断一个对象是否能够被回收。
GC-ROOT类似于一棵数,所有的对象如果能够到根节点说明依然被引用,反之说明不会被根节点引用可以清理
能够作为GC-ROOT根节点,一般是下面4种对象
1、虚拟机栈中引用的对象
2、类中静态属性引用的对象
3、方法区中的常量
4、本地方法栈中Native方法引用的对象
以上是关于JAVA虚拟机理解的主要内容,如果未能解决你的问题,请参考以下文章