什么是JVM内存结构?
jvm将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区;
- 程序计数器:线程私有的,是一块很小的内存空间,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址;
- 虚拟机栈:线程私有的,每个方法执行的时候都会创建一个栈帧,用于存储局部变量表、操作数、动态链接和方法返回等信息,当线程请求的栈深度超过了虚拟机允许的最大深度时,就会抛出StackOverFlowError;
- 本地方法栈:线程私有的,保存的是native方法的信息,当一个jvm创建的线程调用native方法后,jvm不会在虚拟机栈中为该线程创建栈帧,而是简单的动态链接并直接调用该方法;
- 堆:Java堆是所有线程共享的一块内存,几乎所有对象的实例和数组都要在堆上分配内存,因此该区域经常发生垃圾回收的操作;
- 方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据。即永久代,在jdk1.8中不存在方法区了,被元数据区替代了,原方法区被分成两部分;1:加载的类信息,2:运行时常量池;加载的类信息被保存在元数据区中,运行时常量池保存在堆中;
JVM包含哪几部分
JVM主要由四大部分组成:
- 类加载器
- 运行时数据区
- 执行引擎
- 本地库接口
JVM是执行java程序的虚拟计算机系统,执行过程:准备好编译好的Java字节码文件,先通过类加载器将class文件加载到内存中(运行时数据区),但是字节码文件是JVM定义的一套指令规范,并不能直接交给操作系统去执行,因此需要特定的命令解释器(执行引擎)将字节码翻译成特定的操作系统指令集交给CPU去执行,这个过程会需要调用到一些不同语言为Java提供的接口,这就用到了本地接口(本地库接口)。
JVM是如何运行的
- JVM装入环境和配置
- 装载 JVM
- 初始化 JVM,获得本地调用接口
- 运行 Java 程序
JVM的内存分布情况
方法区:用来存储已经被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据。
堆:存放对象实例
虚拟机栈:描述的是每个方法执行的线程内存模型:每个方法被执行的时候,java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作树栈,动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
本地方法栈:与虚拟机栈发挥的作用是相似的,区别只是虚拟机栈为虚拟机执行java方法服务,本地方法栈则是为虚拟机使用到的本地方法服务
程序计数器:是一块较小的空间,它可以看作是当前线程所执行的字节码的行号指示器。在 Java 概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。
没有程序计数器会怎么样
没有程序计数器,Java 程序中的流程控制将无法得到正确的控制,多线程也无法正确的轮换。
局部变量存放在哪里
虚拟机栈
与程序计数器一样,java虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机描述的是java方法执行的线程内存模型,每个方法被执行的时候,java虚拟机都会同步创建一个栈帧,用于存储局部变量表,操作数栈,动态连接,方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种java虚拟机基本数据类型,对象引用和returnAddress类型。
Java代码的编译过程
从java的总体结构来看,编译过程大致分为一个准备过程和3个处理过程。
介绍一下对象的实例化过程
对象实例化过程,就是执行类构造函数对应在字节码文件中的
垃圾回收
怎么定义垃圾
引用计数算法
在对象中添加一个引用计数器,当有一个地方引用它,计数器就加一。引用失效时,计数器值减一。
java中没有使用
单纯的引用计数很难解决对象之间的相互引用的问题。
可达性分析算法
当前商用的程序语言内存管理子系统,都是通过可达性分析算法来判定对象是否存活的。这个算法的基本思路是通过一系列称为“GC ROOTs”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC ROOTs之间没有任何引用链相连,则证明此对象是不可能再被使用的。
回收方法区
方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。
怎么回收垃圾
分代收集理论
虚拟机的垃圾收集器,大多数都遵循分代收集理论。
收集器将java堆划分出不同的区域,然后将回收对象根据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
标记-清除算法
算法分为标记和清除两个阶段。
执行效率不稳定。
空间碎片化问题
标记-复制算法
将内存容量分为凉快,每次只用其中的一块。当这块内存使用完了,就将还活着的对象复制到另外一块上面。
没有空间碎片,但是可用内存就缩小为原来的一半,造成了空间浪费。
标记-整理算法
标记的过程和标记清除算法一样,但是后续的过程不是直接对可回收对象进行清理 ,而是让所有存活对象都向内存一端移动。
Full GC 会导致什么
Full GC期间,全程暂停用户的应用程序。
新生代为什么要分为Eden和Survivor
实际上,新生代中的对象有98%熬不过第一轮收集,因此并不需要按照1:1的比例来划分新生代的内存空间。
提出了Appel式回收,具体做法是把新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只使用Eden和其中一块Survivor。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理到Eden和已经用过的那块Survivor空间。
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代中可用内存空间为整个新生代容量的90%。
G1垃圾收集器
G1是一款主要面向服务端应用的垃圾收集器。
G1仍是遵循分代收集理论设计的,但其堆内存的布局与其他收集器有非常明显的差异:G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的java堆划分为多个大小相等的独立区域,每一个区域都可以根据需要,扮演新生代的Eden空间或者老年代空间。