《深入理解Java虚拟机》 Java对象的生命周期

  • Java虚拟机运行时数据区

    • 方法区:存储 类信息、常量、静态变量、即使编译器编译后的代码等数据,也有别名叫做非堆。  方法区其中有包含有 运行时常量池,用于存放编译期生成的各种字面量和符号引用。其中,可通过String.intern()方法将字符串放入运行时常量池中。
    • 堆:存储的是类实例对象,数组。  JVM 所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。  从内存回收的角度来看,由于现在收集器基本都采用 分代收集算法,所以堆可以细分为 新生代 和老年代;再细分 新生代可以分为:Eden空间,From Survivor空间和To Survivor空间等。
    • 虚拟机栈:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程。
    • 本地方法栈: 本地方法栈服务于虚拟机执行Native方法服务。作用与虚拟机栈相似。
    • 程序计数器:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。每条线程都需要有一个独立的程序计数器  
  • Java类加载机制
    • 装载

      • 加载方式:

        • 从本地系统中直接加载
        • 通过网络下载class文件
        • 从归档文件中加载class文件
        • 从专有数据库中提取class文件
        • 将Java源文件 动态编译为class文件,也就是运行时计算而成
        • 从加密文件中获取
    • 连接
      • 验证
      •  验证java版本号,文件格式,
            元数据校验(是否有父类,是否继承了final类等java 语法) 
            字节码验证(运行检查,栈数据类型和操作码操作参数是否吻合)
      • 准备
        •   private static final int a =1;  constantValue 通知虚拟机生成常量赋值,不需要开辟内存。  基于final static 修饰的 基本数据类型和String起作用
      • 解析
        •   将常量池内的符号引用转变成直接引用
    • 初始化
      • 初始化什么时候被触发?    类 主动使用到的时候
                      1 创建类的实例,也就是new
                      2 访问某个类或者接口的静态变量,给该静态变量赋值
                      3 调用类的静态方法
                      4.反射 (class.forname("..."))
                      5.初始化某个类的子类,则其父类也会被初始化
                      6.java 虚拟机启动时被标明为启动类的类(如springboot启动类)
    • 使用
    • 卸载
      •   1.该类所有的实例都已经被回收,即java堆中不存在该类的任何实例
             2. 加载该类的classloader  已经被回收
             3. 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
  • 类加载器
    • 类加载器的加载特性:
      •  全盘类型机制 
      • 父类委托   
      • 缓存机制    
    • 加载完之后开始使用类,此时需要运行时数据区
      1. PC寄存器
      2. 本地方法栈
      3. 虚拟机栈
      4. 堆:装载的时候,存储所有class实例  空间不足抛出oom
      5. 方法区 :
        1. 线程共享区域,class结构信息,运行时常量池,方法,构造器,方法数据,静态定义
        2. 内存不够时,抛出OOM
      6. 运行时常量池(在方法区中):包含字符串常量池
    • 动态链接:
       符号引用变成直接引用会改变这个动态链接属性
  • Java对象内存布局
    •   Java对象内存分为三部分: 对象头,实例数据,对齐填充
    • 对象头

      •   Mark Word : 哈希码,分代年龄,线程持有的锁,偏向锁ID,偏向时间戳,锁状态,还有1bit的占位符
      •   class Pointer: 指的是类型指针,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
    • 实例数据

      •   对象真正存储的有效信息,代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。这部分的存储顺序会受到虚拟机分配策略参数和字段在Java远吗中定义顺序的影响。HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops,从分配策略中可以看出,先攻宽度的字段总是被分配到一起的。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。
    • 对齐填充
      •   HotSpot VM 要求对象大小必须是8字节的整数倍
  • 对象定位方式:
    •   建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作对上的具体对象。目前主流的访问方式有两种:使用句柄和直接指针两种方式
    • 使用句柄访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾回收)时,只会改变句柄中的实例数据指针,而reference本身不修改。

    • 使用直接指针访问方式的好处是速度更快,节省了中间转发访问的步骤。
  • Java对象的生命周期
    • 创建阶段
    • 应用阶段
    • 不可见阶段
    • 不可达阶段
    • 收集阶段
    • 终结阶段
    • 空间重分配阶段
    • 对象创建过程:
    • 判断对象是否已经“死”了的算法有两种: 
      •   引用计数算法: 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的情况,都是不可能再被使用的。

        •   引用计数算法的缺陷就是它很难解决对象之间相互循环引用的问题
      • 可达性分析算法:通过一系列的成为“GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC roots没有任何引用链相连时,则证明此对象是不可用的。
      • 在Java中,可作为GC roots的对象包括下面几种:
        •   虚拟机栈(栈帧中的本地变量表)中引用的对象
        •   方法区中类静态属性引用的对象
        • 方法区中常量引用的对象
        • 本地方法栈中JNI(即一般说的Native方法)引用的对象。
    • 引用分类:

      • 强引用:只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象

      • 软引用:对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收
      • 弱引用: 被弱引用关联的对象只能生存到下一次GC发生之前。
      • 虚引用: 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
    • 垃圾收集算法
      •   标记-清除算法

        •   不足:

          •   效率问题:标记和清除两个过程的效率不高
          • 空间问题:标记清除后会产生大量不连续的内存碎片,碎片太多导致以后再程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
      • 复制算法:将可用的内存按容量划分大小相等的凉快,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,简单高效。
        • 不足:

          •   当对象存活率较高时,要进行较多的复制操作,效率会降低。
      •   标记-整理算法:标记过程仍然与”标记-清除算法一样,后续让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
      • 分代收集算法:
      • 这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成。而老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。
(0)

相关推荐

  • java对象自救

    原文链接:https://blog.csdn.net/HWHuangeian/article/details/49181807 展开全文 打开CSDN,阅读体验更佳 java对象在gc时的自救小dem ...

  • JVM总结

    JVM总结

  • 六 领域驱动设计-领域对象的生命周期

    每个对象都有生命周期,如图6-1所示.对象自创建后,可能会经历各种不同的状态,直至最终消亡--要么存档,要么删除.当然,很多对象是简单的临时对象,仅通过调用构造函数来创建,用来做一些计算,而后由垃圾收 ...

  • 对象的生命周期

    在解释"对象的生命周期"前,先来看下面这个例子: 有一个停车场共50个停车位,假如这个停车场是通过人工来管理停车位的使用情况,管理员有一个计数器,用来计录当前空闲的停车位有多少个, ...

  • 深入理解Java虚拟机 &GC分代年龄

    转自:https://www.cnblogs.com/xiarongjin/p/8309839.html 堆内存 Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象. ...

  • 深入理解Java虚拟机系列笔记

    类加载过程 最近开始学习Java虚拟机,今天学习了类加载的三个过程,遂写一篇博客作为学习笔记 类加载子系统概述 类加载子系统作为JVM的一部分,负责将硬盘中的class字节码文件加载到JVM中.类加载 ...

  • 深入理解java虚拟机

    本博客所有内容为阅读<深入理解java虚拟机>小结,如有侵权,请联系删除. 运行时数据区域 线程共享的数据区 堆 对于java应用程序来说,堆是虚拟机所管理的内存中最大的一块.虚拟机启动时 ...

  • 2021最新 Java虚拟机(JVM)面试题精选(附刷题小程序)

    推荐使用小程序阅读 为了能让您更加方便的阅读 本文所有的面试题目均已整理至小程序<面试手册> 可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验! 目录 推荐使用小程序阅读 1. J ...

  • Java跨平台原理与Java虚拟机(JVM)

    Java跨平台原理(字节码文件.虚拟机) C/C++语言都直接编译成针对特定平台机器码.如果要跨平台,需要使用相应的编译器重新编译. Java源程序(.java)要先编译成与平台无关的字节码文件(.c ...

  • Java并发编程实战(5)- 线程生命周期

    在这篇文章中,我们来聊一下线程的生命周期. 目录 概述 操作系统中的线程生命周期 Java中的线程生命周期 Java线程状态转换 运行状态和阻塞状态之间的转换 运行状态和无时限等待状态的切换 运行状态 ...

  • Java 虚拟机运行时数据区详解

    本文摘自深入理解 Java 虚拟机第三版 概述 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...