JVM学习篇(1)之组成结构
2016-01-21 16:37
316 查看
JVM的组成
JVM由4大部分组成1. ClassLoader
2. Runtime Data Area
3. Execution Engine
4. Native Interface。
1. ClassLoader:是负责加载class文件,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution
Engine决定。
2. Execution Engine:是执行引擎。Class文件被加载后,会把指令和数据信息放入内存中,Execution Engine则负责把这些命令解释给操作系统。
3. Native Interface :负责调用本地接口的。他的作用是调用不同语言的接口给JAVA用,他会在Native
Method Stack中记录对应的本地方法,然后调用该方法时就通过Execution Engine加载对应的本地lib。
4. Runtime Data Area :是存放数据的。
Java内存
运行时数据区
1. 程序计数器
1) 作用:当前线程所执行的字节码的行号指示器,通过计数器来选取下一条需要执行的字节码指令。
2) 线程私有:每个线程都有一个独立的程序计数器。且互不影响、独立存在。
3) Java方法—------正在执行的字节码指令地址。
Native方法----------空指针
2. Java虚拟机栈
1) 线程私有:生命周期同线程。一个线程对应一个栈。栈里存储的是栈帧。
2) 一个方法对应一个栈帧。
a) 栈帧中包含:局部变量,执行环境,操作数栈。
b) 方法从调用到完成对应栈帧在虚拟机中的入栈、出栈。
3) 局部变量表:用来存储一个类的方法中所用到的局部变量。在编译期分配大小,且大小固定。
4) 执行环境:用于保存解析器对于java字节码进行解释过程中需要的信息,包括:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针
5) 操作数栈:用于存储运算所需要的操作数和结果
3. 本地方法栈
与Java虚拟机栈类似,为Java虚拟机使用到的Native方法服务。
4. Java堆:
1) 作用:是用来存放对象信息的,和Stack不同,Stack代表着一种运行时的状态。换句话说,栈是运行时单位,解决程序该如何执行的问题,而堆是存储的单位,解决数据存储的问题
2) 线程共享
3) 用于存放实例对象,是垃圾收集器管理的主要区域。
5. 方法区(非堆内存)
1) 线程共享,用于存放Class字节码文件的数据结构。
2) 其中包含运行时常量池
a) 用于存放编译期生成的字面变量和符号引用。
b) 具有动态性,运行期间也可能将新的常量放入池中。
JVM中对象的探秘(发生在堆中)
1. 对象的创建1) 检查这个指令的参数是否能在常量池中定位到一个类的符号引用,同时检查这个符号引用所代表的类是否已被加载、解析和初始化。若没有则加载。
2) 为新生对象分配内存的:(对象大小在加载后即可确定)
方式:
a) 指针碰撞式:适用于内存规整。
b) 空闲链表式:适用于内存不规整
安全:
a) 对分配内存空间的动作同步处理。
b) 内存的分配按照线程划分到不同的空间中。由此每个线程在Java堆中需预先分配一块小的内存,叫本地线程分配缓冲。
3) 初始化默认值为0
4) 为对象设置必要的信息。这些信息存储在对象头中。通过对象头可获得对象的信息。
5) 执行init方法初始化
2. 对象在内存中的存储结构
对象头、实例数据和对其填充
1) 对象头
2) 实例数据:对象在堆中真正存储的有效信息。(程序中定义的各种类型字段,包括父类的。)
3) 对其填充:保证8字节的整数倍。
3. 如何定位对象
Java程序通过栈上的referenc数据来操作堆上的对象。操作方式:
1) 句柄访问:
优点:对象移动时只需修改实例指针即可,不用修改reference。
2) 直接指针
优点:效率高,加少了一次寻址的时间。
垃圾收集器与内存分配策略
内存的回收
判断对象是否可被回收
堆1. 引用计数算法
1) 思想:给对象中添加一个引用计数器,没放有一个地方引用它时,计数器值就+1;引用失效时,计数器值就-1;当计数器的值为0时表示不再被任何对象引用。
2) 优点:简单、高效
3) 缺点:不能解决循环引用的问题。(主流Java虚拟机中没有使用这个方法)
2. 可达性分析算法:(实际使用的方案)
1) 思想:
通过一系列的称为“GC Root”的对象作为起点,从这些结点开始向下搜索,搜索所走的路径称为【引用链】,当一个对象到GC Root没有任何引用链相连时,则此对象是不可用的。
2) 哪些可以是“GCRoot”
a) Java虚拟机栈(栈帧中本地变量表)中的引用(referencce)
b) 本地方法栈中的引用(reference)
c) 方法区中类的静态属性引用
d) 方法区中常量引用
3. 引用的分类
1) 强引用
类似于Object obj = new Object();
2) 软引用
用来描述一些有用但非必须的对象。有SoftReference类实现。
回收时机:将要发生内存溢出异常之前,将这些对象列近回收范围中,之后进行第二次回收。
3) 弱引用
用来描述非必须对象,强度比软引用更若一些。由WeakReference类实现。
只能生存到下一次垃圾收集之前。
回收的时机:当垃圾收集器工作时,无论当前内存是否足够,都会被回收。
4) 虚引用
由PhantomReference类实现。
方法区
主要回收两部分:废弃常量和无用的类。
1. 废弃常量:
判定:与堆中的对象类似。没有任何String对象引用。
2. 无用类
判定:
1) Java堆中不存在任何该类的对象。(对象没有)
2) 该类的Class对象没有在任何地方被引用。(Class不被引用)
3) 该类的ClassLoader已被回收。(加载器没了)
回收算法
3种回收算法
1. 标记-清理算法 (最基本的回收算法)1) 思想:分为标记和清除两个阶段
首先标记出所有需要回收的对象(即标记那些可被回收的对象),在标记完后统一回收所有被标记的对象
2) 效率低、产生大量不连续的内存碎片。
2. 复制算法 (解决上一算法中的效率问题)
1) 思想:将内存分为大小相等的两个部分,每次使用一个部分。当其中一块用完了,就将还存活的对象复制到另一块中(这一步也起到了整理碎片的目的),然后对使用过的内存空间一次性清理掉。
2) 优点:简单、高效
3) 缺点:内存为原来的一半。
4) 实际使用:
a) 使用用于新生代的回收
b) 改进:
将内存分为一大二小3块空间。Eden和2个Survivor。(8:1)
回收时将Eden和Survivor中还存活的对象一次性地复制到另一块Survivor中,然后清理。
如果另外一块Survivor空间没有足够空间存放上一次新生代收集的存活对象时,将多余的通过分配担保机制进入老年代。
3. 标记-整理算法
1) 使用于老年代
2) 思想:标记,然后将所有存活的对象都想一端移动,最后直接清理掉端边界以外的内存。
注:新生代和老年代根据对象的存活周期长短来划分的。
HotSpot的算法实现 (如何高效执行GC)
1. 枚举GC Root结点 (如何枚举)1) 方法:借助一个OopMap的数据结构来存储哪些地方存放着引用。
2. 安全点 (如何保证一致性)
1) 原因:在枚举结点的时候需要保证一致性。以保证在枚举结点时引用关系不发生变化。保证一致性的措施是暂停所有Java执行的线程。
2) 作用:只有达到安全点时才停顿,开始GC
3) 安全点的选取:在指令复用的地方设置。
4) 在GC发生时如何让所有线程都跑到安全点:
a) 抢先式:先全部中断,如果有没到安全点的在回复线程让它跑到安全点。 (几乎不采用)
b) 中断式:发生GC中断时不直接对线程操作,通过设置一个标记。各线程执行时主动轮询这个标记,若标记为真则自己中断线程。
轮询的地方 = 安全点 + 对象分配内存地方
3. 安全区
1) 安全区内不会发生引用关系的变化。
2) 线程在安全区内任何地方都能随时中断。
3) 结合安全区的完整停顿过程:
a) 线程进入安全区时,标记自己。
b) 发生GC时,不用管标示自己已进入安全区的线程状态
c) 线程离开安全区时,检查是否已完成了根结点的枚举。
完成--à 离开
未完成----à等到完成后再离开
对象销毁的过程(经过两次标记)
1. 寻找:通过第一步(可达性分析)寻找没有与“GC Root”相关联的引用。(第一次标记)2. 筛选:筛选的条件是此对象是否有必要执行finalize()方法。
当对象没有重写finalize()方法或已经被虚拟机调用过,视为“没必要执行”。
3. 销毁:
1) 将上一步筛选出的对象放入到F-Queue的队列中。
2) JVM创建一个低优先级的Finalizer线程来执行其中对象的finalize()方法。
3) 若有的对象finalize()方法执行时间过长,将会跳过这个对象,稍后GC会对F-Queue中的对象进行第二次小规模标记。(第二次标记)
4) 在第二次回收之前还没有被其他引用链接的话,在第二次回收时就被真的回收了。
内存的分配
1. 对象优先在Eden分配
对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机发起一次Minor GC(新生代GC)。
2. 大对象直接进入老年代
大对象:需要大量连续内存空间的Java对象。
目的:可以避免在Eden区及两个Survivor区之间发生大量的内存赋值。
3. 长期存活的对象将进入老年代
区分:
1) 为每个对象定义一个年龄(Age)计数器。
2) 每经过一次Minor GC,年龄+1。
3) 达到阈值(15)时,移至老年代。
4. 动态年龄判定
设A=Survivor空间中相同【年龄】对象个数大于一般。年龄大于A的对象也可直接进入老年代。
5. 空间分配担保 (老年代为新生代担保)
发生Minor GC之前,查询老年代最大连续空闲空间OM
若OM > 新生代所有对象总和NM
Minor GC 安全,可以执行
否则
查看是否允许担保失败
允许:
若OM > 每次晋升对象的平均大小pM
执行Minor GC
否则
执行一次FullGC(老年代GC)让老年代腾出更多空间
不允许
执行一次FullGC(老年代GC)让老年代腾出更多空间
相关文章推荐
- Java 6 JVM参数选项大全(中文版)
- 深入解析JVM对dll文件和对类的装载过程
- Java虚拟机JVM性能优化(二):编译器
- Java程序员必须知道的5个JVM命令行标志
- Java虚拟机JVM性能优化(三):垃圾收集详解
- 解析Java虚拟机中类的初始化及加载器的父委托机制
- JAVA中JVM的重排序详细介绍
- Java虚拟机JVM性能优化(一):JVM知识总结
- Android Studio 报错failed to create jvm error code -4的解决方法
- 解析Linux系统中JVM内存2GB上限的详解
- 了解Java虚拟机JVM的基本结构及JVM的内存溢出方式
- Java堆空间占满的gc日志实例
- java动态代理模式
- Groovy Meta Object Protocol
- xms/xmx/xss在kette中的调优设置
- JVM工作原理和特点
- JVM性能调优监控工具jps、jstack、jstat、jmap、jinfo使用详解
- jmap详解
- 深入Java虚拟机
- JVM内存模型及垃圾收集策略解析