Java中的几种生命周期比较

2023年 9月 28日 106.3k 0

Java对象的生命周期

概述

Java对象的生命周期包括以下6个阶段:

  • 创建阶段(Created)
  • 应用阶段(In Use)
  • 不可见阶段(Invisible)
  • 不可达阶段(Unreachable)
  • 收集阶段(Collected)
  • 终结阶段(Finalized)
  • 创建阶段

    即对象的声明和构造阶段,主要过程如下:

    • 在内存中分配空间
    • 开始构造对象
    • 从超类到子类进行static成员初始化(加载静态变量)
    • 从超类开始调用构造方法(双亲委派机制)
    • 子类成员变量初始化,子类构造方法调用

    一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段。

    应用阶段

    对象至少被一个强引用持有

    不可见阶段

    即对象超出了所在的作用域(对象在某方法中创建并引用,某方法已调用完毕,对象进入不可见阶段,无法再被引用)

    不可达阶段

    对象处于不可达阶段是指该对象不再被任何强引用所持有。

    与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用,这种情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。

    收集阶段

    当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。

    终结阶段

    当对象执行完finalize方法后进入不可达状态,等待GC回收

    线程的生命周期

    概述

    线程的生命周期包含6种状态:

    • 新建(NEW)
    • 可运行(RUNNABLE)
    • 阻塞(BLOCKED)
    • 等待(WAITING)
    • 计时等待(TIMED_WAITING)
    • 死亡(TERMINATED)

    当线程进入运行状态后,一般的操作系统是采用抢占式(两种线程调度模式之一,另外一种为分时调度)的方式来让线程获得CPU。所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞、就绪之间切换。

    新建(NEW)

    使用new方法,new出来线程,此时仅仅由JAVA虚拟机为其分配内存,并初始化成员变量的值。此时未调用start方法,程序未运行,程序进入RUNNABLE状态。

    可运行(RUNNABLE)

    Java 中的 Runable 状态对应操作系统线程状态中的两种状态,分别是 Running(运行中) 和 Ready(等待CPU分配资源),也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在执行,正在等待被分配 CPU 资源。

    所以,如果一个正在运行的线程是 Runnable 状态,当它运行到任务的一半时,执行该线程的 CPU 被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是 Runnable,因为它有可能随时被调度回来继续执行任务。

    阻塞状态(BLOCKING)

    区别于可运行的RUNNABLE状态,另外有三种情况导致线程无法运行,分别为:BLOCKED(被阻塞)、WAITING(等待)、TIMED_WAITING(计时等待),可统称阻塞状态(BLOCKING)

    被阻塞(BLOCKED)

    当处于RUNNABLE状态的线程,因为某些原因(没有拿到同步锁或者IO请求被阻塞),放弃了CPU的使用,此时进入BLOCKED状态,暂停运行,JVM也不会再分配CPU给线程,直到线程重新回到RUNNABLE状态。

    等待(WAITING)

    运行中的程序由于某些条件未满足,主动调用Object.wait()/Thread.join()/LockSupport.park()等方法,程序进入WAITING状态(放入对象等待池中),等待其他线程的唤醒。

    计时等待(TIMED_WAITING)

    调用wait方法时加入了等待时长参数,或者调用了sleep方法,进入计时等待,超时后自动被系统唤醒,也可以被notify提前唤醒

    死亡状态(TERMINATED)

    • run方法执行完毕,线程正常退出
    • 出现未捕获的异常,线程意外停止

    以上两种情况将导致线程终止

    线程间切换

    • BLOCKED进入RUNNABLE
      • 线程获取锁后即可从BLOCKED进入RUNNABLE状态,BLOCKED无超时机制,如没拿到锁将一直阻塞
    • WAITING/TIMED_WAITING进入RUNNABLE
      • 等待中的线程首先被notify/notifyALl唤醒,进入BLOCKED阻塞状态,获取锁后重新进入RUNNABLE状态

    Spring Bean的生命周期

    image.png

    概述

    Spring Bean的主要生命周期为以下四个阶段:

  • 实例化(分配空间)
    2. 前置/后置处理器
  • 属性赋值(get/set/依赖注入)
  • 初始化(init方法)
    5. 前置后置处理器
  • 销毁(destroy方法)
  • Servlet生命周期

    Servlet生命周期主要为以下四个阶段:

    • 加载阶段(Loading)
    • 初始化阶段(Initialization)
    • 处理请求阶段(Request Handling)
    • 销毁阶段(Destruction)

    加载阶段(Loading)

    服务器启动时(第一次访问时,如果在注解中设置loadOnStartup为一个非-1的值,则设置为服务器启动时加载并创建实例),会加载 Servlet 类并创建对应的 Servlet 实例。这个过程只会执行一次(懒加载机制)。

    • 优点:节约内存开销
    • 缺点:第一个用户的体验感不好
    • 把注解加上启动参数loadOnStartup 非负数的时候容器加载的时候就启动了
    @WebServlet(value = "/myServlet",loadOnStartup = 0)
    

    初始化阶段(Initialization)

    在 Servlet 实例被创建后,容器会调用其 init() 方法来完成初始化操作。在初始化期间,Servlet 可以读取配置参数和其他上下文信息,并准备好处理客户端请求。

    处理请求阶段(Request Handling)

    在 Servlet 初始化完成后,容器会将客户端的请求转发给 Servlet 实例,并调用其 service() 方法来处理请求。service() 方法会根据请求的类型(GET、POST 等)来调用对应的 doGet()、doPost() 等方法进行处理。可以访问多次

    销毁阶段(Destruction)

    当 Servlet 实例不再需要时,容器会调用其 destroy() 方法来释放资源。这个过程只会执行一次。(也可以手动调用destroy方法)

    注意

    Servlet 生命周期中的各个阶段并非一成不变的。在某些情况下,例如 Servlet 初始化失败或容器需要重新加载 Servlet 类时,Servlet 生命周期可能会被打断或重新开始。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论