JVM虚拟机

Java在各个平台上是如何跑起来的?

我们都知道 Java 源文件,通过编译器,能够生产相应的.Class 文件,也就是字节码文件, 而字节码文件又通过 Java 虚拟机中的解释器,编译成特定机器上的机器码

不同平台上的解释器不同,但是虚拟机环境是相同的,这就是java为什么能在各个平台上运行的原因

 

JVM允许一个应用并发执行多个线程

JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区 域【JAVA 堆、方法区】、直接内存

JVM的多线程处理提高了程序的并行处理效率

 

JVM介绍

 

程序计数器(线程私有)

一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为线程私有的内存

正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址),如 果还是 Native 方法,则为空

这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域

 

程序计数器的作用是用于存储下一条指令(即java代码编译完成的字节码指令)的地址,方便虚拟机解释器找到这些指令并且翻译成机器码交给CPU执行

 

虚拟机栈(线程私有)

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息

每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈入栈到出栈的过程

一个方法域都对应一个栈帧,每次方法执行结束,栈帧内存放的变量引用都会被GC机制回收,回收结束后,对应的强引用也会消失(不可达),便于GC机制继续回收那些不可达的对象占用的堆内存

 

JVM内存分布

 

 

本地方法区(线程私有)

本地方法栈则为 Native 方法服务,调用本地其它语言的方法,例如C++,C等

Java提供了本地调用需要实现的接口(JNI)Java Native Interface ,它允许Java代码和其他语言写的代码进行交互

 

堆(Heap-线程共享)-运行时数据区

是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行 垃圾收集的最重要的内存区域

堆内存通常存放new关键字创建的对象数据

 

堆内存分为 新生代 (Eden 区、From Survivor 区和 To Survivor 区)和 老年代

 

在了解堆内存内的分代回收机制前,先要了解下垃圾回收机制如何判断一个对象是否存活

第一次标记

如果对象在进行可达性分析后发现没有与GC Roots(对象引用关系链,相当于一个树结构)相连接的引用链, 那它将会被第一次标记,随后进行一次筛选

GCroot原理:通过对枚举GCroot对象做引用可达性分析

可以作为GC root对象的有:

  • 局部变量(Object obj = new Object() 中的obj)引用的对象
  • 静态属性引用的对象 (static修饰的属性)
  • 方法区常量引用的对象(static+final修饰的属性)
  • 本地方法栈中引用的对象

 

 

GC垃圾回收机制

 

GC机制在新生代中的运行过程

1、将Eden区和ServivorFrom区中的数据复制到ServivorTo区,对象年龄+1,如果有达到老年区对象标准的移至老年区(默认15次扫描后)

2、清空Eden区和ServivorFrom区数据

3、替换ServivorFrom区和ServivorTo区数据内容

 

在第一次标记的同时我们要明白GC机制还做了一件事情:

判断是否要执行finalize()方法

 

finalize()方法

finalize()是Object的protected方法

finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性

 

 

 

 

对象可由两种状态,涉及到两类状态空间

一是终结状态空间 F = {unfinalized, finalizable, finalized}

二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}

各状态含义如下:

unfinalized: 新建对象会先进入此状态,GC并未准备执行其finalize方法,因为该对象是可达的 finalizable: 表示GC可对该对象执行finalize方法,GC已检测到该对象不可达。正如前面所述,GC通过F-Queue队列和一专用线程完成finalize的执行 finalized: 表示GC已经对该对象执行过finalize方法 reachable: 表示GC Roots引用可达 finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达 unreachable:对象不可通过上面两种途径可达

 

第二次标记

第二次标记会为执行完finalize()方法的对象进行最后一次标记,之后销毁该对象内存

 

老年代

新时代与老年代的内存比例为1:2

老年代的对象比较稳定,所以 MajorGC 不会频繁执行

MajorGC(采用标记复制回收策略)触发条件

频繁触发MajorGC的原因一般为:新生代内存不足以存放过大内存占用率的对象,或者是没有合理分配好新生代的对象内存

 

参考博客

 

方法区/永久代(线程共享)

元空间并不在虚拟机中,而是使用本地内存,其大小仅受本地内存限制

 

G1 收集器

G1 收集器 Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与 CMS 收集器,G1 收 集器两个最突出的改进是:

  1. 基于标记-整理算法,不产生内存碎片
  2. 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收

 

G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域 的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域,区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率