JVM

1.java虚拟机执行java程序的过程

Java源代码文件(.java)会被Java编译器编译为字节码文件(.class)

.class 文件 由JVM中的类加载器加载各个类的字节码文件,

加载完毕之后,交由JVM执行引擎执行。

1.1 JVM生命周期

  • 启动

启动一个Java程序时,一个JVM实例就产生了,
任何一个拥有public static void main(String[] args)* 函数的class都可以作为JVM实例运行的起点。

  • 运行

main()作为该程序初始线程的起点,任何其他线程均由该线程启动。

  • 消亡

当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。


1.2 JVM体系结构


1.2.1 类装载器(ClassLoader)(用来装载.class文件)

a. 概述
  1. JVM类加载机制:是虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可被虚拟机直接使用的Java类型的过程。
  2. 特性运行期类加载。即在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期完成的,从而通过牺牲一些性能开销来换取Java程序的高度灵活性。
  3. java 动态扩展特性是依赖于运行期动态加载动态连接实现的

注意:
a. 实际情况下,每个class 文件都可能代表一个类或一个接口
b. class 文件指一串二进制字节流

b. 类加载全过程

类从被加载到虚拟机内存中开始到卸载出内存为止,整个生命周期包括7阶段:

类生命周期

注意:

  • 『加载』->『验证』->『准备』->『初始化』->『卸载』这5个阶段的顺序是确定的,而『解析』可能为了支持Java语言的运行时绑定会在『初始化』后才开始。
  • 上述阶段通常都是互相交叉地混合式进行的,比如会在一个阶段执行的过程中调用、激活另外一个阶段。
  • 有且只有5种情况必须对类立即“初始化”:
    a. 遇到new、getstatic、 putstatic和invokestatic 4条指令时,对应场景是:
    (1) new 实例化对象
    (2) 读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)
    (3) 调用类的静态方法
    b. 使用java.lang.reflect 包方法对类进行反射调用的时候,若类没进行初始化,先出发其初始化
    c. 初始化类时,若发现父类没初始化,先对其父类初始化
    d. 虚拟机启动时,初始化主类(含main()方法的类)
    e. 如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、 REF_putStatic、 REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化

类加载过程:

  • 加载
c. 类加载器&双亲委派模型

1.2.2 执行引擎(执行字节码,或者执行本地方法)


1.2.3 运行时数据区(方法区()、堆、java栈、PC寄存器、本地方法栈)P 3 内存模型

  • 方法区(线程共享)(又称,永久代
    存储已被虚拟机加载类相关信息、常量、静态变量、即时编译器编译后的代码等数据。(存储程序中永远不变的或唯一的内容)
    方法区异常:
    • OutOfMemoryError: 方法区无法满足内存需求时
  • 运行时常量池 是方法区的一部分
    存放编译器生成的各种字面量和符号引用,将在类加载后进入方法区的运行时常量池存放。
  • 堆(线程共享)
    存放对象实例和数组
    是Java 虚拟机所管理的内存中最大的一块; 是垃圾回收器 管理的主要区域
    在虚拟机启动时创建
    可使用物理上不连续的内存空间,逻辑相连即可
    扩容是通过-Xmx和-Xms控制
    堆异常:
    • OutOfMemoryError: 虚拟机栈扩容时不能申请到足够内存空间
  • 虚拟机栈(线程私有)
    存储局部变量表、操作数栈、动态链接、方法出口等信息
    栈由系统自动分配,速度快,连续内存空间
    描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame )。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

虚拟机栈中两种异常:
-StackOveflowError: 线程请求栈深度大于虚拟机栈深度
- OutOfMemoryError: 虚拟机栈扩容时不能申请到足够内存空间

  • 本地方法栈(线程私有)———也会抛出上面两种异常
    虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务
  • 程序计数器(线程私有)
    是当前线程所执行的字节码的行号指示器
    如果线程正在执行的是一个Java方法,那么计数器记录的是正在执行的虚拟机字节码指令的地址
    如果线程正在执行的是一个Native方法,那么计数器的值则为空
运行时数据区

例子

代码

内存数据传递

**注


虚拟机对象

1)对象创建(new)
  • 虚拟机检查new指令参数是否在常量池中定位到类的符号引用,检查符号引用代表的类是否被加载、解析、初始化过。没有, 进行类加载
  • JVM为新对象分配内存。
    a.大小由类加载可知
    b. java堆内存是否规整,决定了java堆内存分配方式
    • 指针碰撞:内存规整(空闲放一边,使用的放一边,中间放指针作为分界点指示器。)
      (内存分配即指针向空闲空间挪动对象大小的距离)
    • 空闲列表: 内存不规整(空闲和使用的内存交错, 维护列表,记录哪些空闲)
      分配是从列表中找到足够大空间
      规整 由垃圾回收器是否带有压缩整理的功能决定
  • 分配内存空间初始化为0
  • jvm对对象进行设置(对象是哪个类实例、对象哈希码等)
2)对象的内存布局(3块区域)
  • 对象头(2部分信息)
    a. Mark Word:用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    被设计出非固定数据结构->在极小空间存储尽量多信息
    b.类型指针:确定对象的所属类(对象是哪个类的实例)
  • 实例数据
    存储真正的有效信息,是程序代码中定义的各种类型的字段内容。存储顺序会受虚拟机分配策略参数字段在Java源码中定义顺序这两个因素影响
  • 对齐填充:占位符,帮助补全未对齐的对象实例数据部分(8字节的倍数),非必需。
3)对象的访问定位

java 程序通过上的reference数据访问堆上的具体对象(2种方式)

  • 句柄
    Java堆中划分出一块内存来作为句柄池 ,** reference存储的是对象的句柄地址**,在句柄中包含了对象实例数据与类型数据各自的具体地址信息。
    通过句柄访问对象

好处:reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要修改。

  • 直接指针访问:
    在Java堆对象的布局中考虑如何放置访问类型数据的相关信息,reference存储的直接就是对象地址。

    直接指针访问对象

    好处:速度更快,节省了一次指针定位的时间开销。


2. Java多线程

Java 虚拟机多线程是通过 线程轮流切换分配处理器执行时间 实现的

3. 垃圾回收器

JVM内存模型中的运行时数据区的5部分:

  • 虚拟机栈、程序计数器、本地方法栈:随线程生死
  • 方法区、堆:分配和回收都是动态的,是GC管理的部分
  • GC功能:
    识别并且丢弃应用不再使用的对象来释放和重用资源

3.1 对象判活(2种方法)

3.1.1 引用计数算法

  • 方法:
    给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
  • 问题:
    难以解决对象间相互循环引用的问题

3.1.2 可达性分析算法

  • 方法:
    通过一系列被称为『GC Roots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
  • 可做GC Roots的对象:
  • 虚拟机栈中引用的对象,主要是指栈帧中的本地变量
  • 本地方法栈中Native方法引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象

引用类型(4种)

  • 强引用(StrongReference)
    • "Object obj = new Object()" 这类引用
    • 具有强引用的对象不会被GC
    • 即便内存空间不足,JVM宁愿抛出OutOfMemoryError使程序异常终止,也不会随意回收具有强引用的对象。
  • 软引用(SoftReference)
    • 只具有软引用的对象,内存空间不足的时候被GC,如果回收之后内存仍不足,才会抛出OOM异常
    • 软引用常用于描述有用但并非必需的对象,比如实现内存敏感的高速缓存
  • 弱引用(WeakReference)
    • 只被弱引用关联的对象,GC时,无论当前内存是否足够都会被GC;
    • 强度比软引用更弱,常用于描述非必需对象。**
  • 虚引用(PhantomReference)
    • 仅持有虚引用的对象,在任何时候都可能被GC
    • 必须和引用队列 (ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
对象“判刑”过程

达性分析算法中, 被判定不可达的对象还未真的判『死刑』
至少要经历两次标记过程:

  • 第一次标记:若对象与GC Roots 没有相连的引用链,且进行第一次筛选
  • 筛选条件: 判断对象是否有必要执行finalize()方法;若被判定为有必要执行finalize()方法,之后还会对对象再进行一次筛选,如果对象能在finalize()中重新与引用链上的任何一个对象建立关联,将被移除出“即将回收”的集合。
  • 注:任何一个对象的finalize()方法只能被系统自动调用一次
回收方法区(2部分)
  • 废弃常量
    与回收Java堆中的对象的GC很类似,即在任何地方都未被引用的常量会被GC。
  • 无用的类: 满足以下三个条件才会被GC:
    • 该类所有的实例都已被回收,即Java堆中不存在该类的任何实例;
    • 加载该类的ClassLoader已经被回收
    • 该类对应的java.lang.Class对象没在任何地方被引用,即无法在任何地方通过反射访问该类的方法。

3.2 垃圾收集算法

根据对象存活周期的不同, 将java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法

  • 新生代: 大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可。
  • 老年代: 对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。
3.2.1 标记—清理算法
  • 思想:
    a.标记需要回收的对象
    b. 统一回收被标记的对象
  • 不足:
    a. 效率问题: 清除和标记效率不高
    b. 空间问题:标记清除后会产生大量不连续的内存碎片
3.2.2 复制算法
  • 思想:
    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象『复制』到另外一块上面,再将这一块内存空间一次清理掉。
  • 优点:
    每次都是对整个半区进行内存回收,无需考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
  • 不足:
    a. 每次可使用的内存缩小为原来的一半,内存使用率低
    b. 存活率较高就需要进行较多复制操作,效率变低
    复制算法

扩展

新生代中的对象98%是“朝生夕死”的,将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1。每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)

3.2.3 标记-整理算法(存活率高适用)
  • 思想:
    『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
  • 优点:即没有浪费50%的空间,又不存在空间碎片问题,性价比较高
    一般情况下,老年代会选择标记-整理算法。
    标记-整理算法

补充

JVM 将堆内存划分为 Eden(年轻代)Survivor(老年代)Tenured/Old(持久代)空间。

  • 年轻代
    所有新生成的对象首先都是放在Eden区。 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象,对应的是Minor GC,每次 Minor GC 会清理年轻代的内存,算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域存放满对象后,就将对象存放到年老代区域

  • 老年代
    在年轻代中经历了N(默认15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越多,我们就需要启动Major GC和Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。

  • 持久代
    用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响。

    堆内存划分

堆内存三个空间分别对应 三个GC: Minor GCMajor GCFull GC

  • Minor GC:
    用于清理年轻代区域。Eden区满了触发一次Minor GC。清理无用对象,将有用对象复制到“Survivor1”、“Survivor2”区中
    (这两个区,大小空间也相同,同一时刻Survivor1和Survivor2只有一个在用,一个为空)
  • Major GC
    用于清理老年代区域
  • Full GC:
    用于清理年轻代、年老代区域。 成本较高,会对系统性能产生影响。
    Old区满了,则会触发一个一次完整地垃圾回收(FullGC)

3.3 HotSpot 算法实现

3.3.1 枚举根节点(找到GC Roots)

主流Java虚拟机使用的都是准确式GC,在执行系统停顿之后无需检查所有执行上下文和全局的引用位置,而是通过一些办法直接获取到存放对象引用的地方,在HotSpot中是通过一组称为OopMap的数据结构来实现的,完成类加载后会计算出对象某偏移量上某类型数据、JIT编译时会在特定的位置记录栈和寄存器中是引用的位置。这样GC在扫描时就可直接得知这些信息,并快速准确地完成GC Roots的枚举

3.3.2 安全点
3.3.3 安全区域
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容