一文了解Hotspot虚拟机下JAVA对象从创建到回收的生命周期
Java虚拟机是Java的核心和基础,他是Java编译器和操作系统平台之间处理器,能实现跨平台运行Java程序。本文主要讲解的是虚拟机如何管理对象,即Java对象在JVM虚拟机中被创建到回收的流程
Java对象从创建到回收的生命周期
- 对象创建流程
- 1.类加载检查
- 类加载过程
- 2.分配内存
- JVM运行时内存数据区布局
- 运行时数据区关系
- 3.初始化
- 4.设置对象头
- 5.执行init方法
- 对象回收流程
- 1.标记存活对象
- 2.非存活对象回收
- 垃圾回收策略
- 垃圾回收算法
- 垃圾回收器
- Serial收集器
- Parallel Scavenge收集器
- ParNew收集器
- CMS
对象创建流程
public class Main {public static void main(String[] args) {createObject();}public static void createObject() {Object object = new Object();}
}
当虚拟机碰到new或者克隆一个类的实例对象时【如上new Object()】,它会经历如下过程:

1.类加载检查
当虚拟机遇到一个new指令的时候,首先去判断这个指令的参数是否能在方法区找到类的引用,并且检查这个引用是否已经被加载,解析和初始化过。如果没有,则必须先执行先执行类的加载过程
类加载过程
参考博客:Java类的加载过程
2.分配内存
当类加载完以后,虚拟机将会为新生对象分配内存。对象所需的内存大小在类加载完后便可完全确定,便会将Java堆中一块确定大小的内存从Java堆中划分出来
此时会涉及两个问题
2.1.虚拟机如何分配内存
2.2.在并发情况下,如何保证多个对象的内存不重叠
JVM运行时内存数据区布局

a)堆区
Java堆是用来存储对象本身的以及数组。堆是被所有线程共享的,在JVM中只有一个堆。堆有分代逻辑,堆划分为了年轻代,老年代;年轻代分为了eden区,s0区,s1区,比例默认为老年代:年轻代=2:1,eden:s0:s1=8:1:1;
这里引发两个问题:
1.为什么堆中要分代?
2.为什么年轻代还要分为3个区?为什么区的比例为8:1:1?
会涉及到关于对象回收的机制:
1.堆中分代是为了提高效率,对象是有生命周期,有的对象存活时间长,有的对象存活时间短,存活时间长的放到老年代;存活时间短的放到年轻代。老年代回收频率低点,年轻代回收频率高点
2.分为3个区是为了gc之后方便把存活的对象复制到s1或者s2中,方便内存整理。比如为8:1:1是因为大多数的对象都是朝生夕死的,存活时间比较短,所以jvm默认8:1:1的比例是合适的,让 Eden区尽量大,survivor区足够用即可

b)栈
Java栈中存放一个个栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括:局部变量表,操作栈,动态链接【指向当前方法所属的类的运行时常量池的方法】,方法出口【方法执行完后需返回地址】

c)本地方法栈
本地方法栈与Java栈作用和原理类似,区别在于Java栈是为执行本地方法服务的,而本地方法栈则是执行本地方法服务的
d)方法区
与堆一样,是线程共享的区域。在方法区中存储了类信息,静态变量,常量以及编译器编译后的代码
e)程序计数器
在jvm中,多线程是通过线程轮流切换来获得cpu执行时间的。为了保证每个线程在切换后能够恢复到切换之前程序执行的位置,每个线程都有自己独立的程序计数器
运行时数据区关系
由下图可以看出:堆和方法区是所有线程共用的,本地方法栈,Java栈,程序计数器是线程私有的

看到堆中存放对象,引发两个问题:
1.所有新创建对象都存放到堆中吗?
2.堆中既有老年代又有年轻代,新创建的对象应该放在哪里
我们先整体看一下对象在内存的分配流程,然后再逐一回答上面的问题

根据图流程所示回答第一个不是所有的对象都会存放到堆中,有可能是栈。栈上分配主要是虚拟机根据该对象是否被外部访问。如果不会逃逸出该对象在栈上分配,随着方法的结束出栈而销毁。
对象逃逸分析:分析对象的动态作用域,当一个对象在方法中被定义后,它可能被外部方法引用,例如被作为参数传递到其他地方去
public User createUser(){User user = new User();user.setName("judy");user.setAge(18);return user;
}public void test1(){User user = createUser();
}
像上面的方法,它可能被其他方法调用,无法在栈内分配;而像作用域非常确定,比如:
public void createUser(){User user = new User();user.setName("judy");user.setAge(18);
}
那这种作用域非常确定,不会被其他方法调用,就可以在栈内分配。
因为栈内存比较小,为了防止栈内没有一大块连续空间导致对象内存不够分配,虚拟机会采用标量替换方法去解决这个问题
标量替换:当对象确定在栈内分配时候,并且开启了标量替换,它不会直接创建对象,因为创建对象需要设置对象头也会占用空间,而是将该Java对象成员变量进行分解,分为若干个被这个方法使用的局部成员变量,然后为分解后的变量分配空间。jdk7以后默认开启。标量一般是指Java中的基本数据类型
解决完第一个问题:对象否则都存在于堆中?我们再来看第二个问题:堆中既有老年代又有年轻代,新创建的对象应该放在哪里?
第二个问题的答案是:一个新创建的对象,如果非常大,超过虚拟机设置的值,就会直接放到老年代;但是如果没有超过限定值,先放到Eden中。至于S0,S1区,对象在通过minorGC以后才会存放到这两个区域中,后续讲解到对象的垃圾回收时,还会重点解决一下。
3.初始化
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值【不包括对象头】。这一步保证对象的实例字段在Java代码中可以不赋初始值就直接使用,程序就能访问到这些字段的数据类型所对应的零值
4.设置对象头
对对象的实例属性值设置为零值后,需要对对象进行必要的设置,例如这个对象属于哪个类的实例,如果才能找到类的元数据信息,对象的哈希码,对象gc分代信息。这些信息在对象的对象头中。
对象分为三部分信息:对象头,实例数据,对象填充
4.1对象头:分为三部分信息
4.1.1第一部分存储对象自身运行时的数据
4.1.2第二部分存储类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象属于哪个类的实
4.1.3第三部分是数组长度,若为数组则有数,若没有则为空

4.2实例数据:对象的实例属性
4.3对象填充:当对象头+实例数据的字节数不是8字节的整数时,字节填充使其为8字节的整数倍,加快内存寻址
5.执行init方法
为对象赋值和执行构造方法
对象回收流程
1.标记存活对象
对象回收之前,先判断对象是否活着,判断对象是否活着两种方式
1.引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;
当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
2.可达性分析算法
将"GC Roots"对象作为起点,从这些起点开始向下搜索引用的对象,找到的对象是非垃圾的对象,其余标记为垃圾对象。
GC Roots根节点:线程栈的本地变量,静态变量,本地方法栈中的变量
其中引用关系可以分为:强引用,软引用,弱引用,虚引用
强引用:普通的变量引用
public static User user=new User();
软引用:被SoftReference对象包裹着,正常情况不会被回收,但是gc做完后发现释放不出空间存放新的对象,则会将这些软引用对象回收掉。用来实现内存敏感的高速缓存
public static SoftReference<User> user=new SoftReference<User>(new User());
弱引用:将对象用WeakReference软引用类型的对象包裹着,弱引用跟没有引用差不多,GC会直接回收掉。
虚引用:最弱的引用,几乎不用
引发一个问题,是否没有可达性分析分析到的对象就会被回收?
答案是不会,一个对象被回收要经历两次标记的过程
1.第一次标记进行筛选
筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,对象将直接被回收。
2.第二次标记
第二次标记如果这个对象覆盖了finalize方法,finalize方法是对象脱逃死亡命运的最后一次机会,如果对象要在finalize()中成功拯救
自己,只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,
那在第二次标记时它将移除出“即将回收”的集合。
如果对象这时候还没逃脱,那基本上它就真的被回收了。
注意:
一个对象的finalize()方法只会被执行一次,
也就是说通过调用finalize方法自我救命的机会就一次。
2.非存活对象回收
被标记的对象是如何在内存中被回收,首先堆在内存是分代的,两者根据对象生命周期的不同,存放着不同类型的对象。那意味着也需要采取不同的回收策略【年轻代回收和老年代回收】,回收策略也有着相应的回收算法,根据不同垃圾回收算法JVM也提供了一系列垃圾回收器
垃圾回收策略
Minor GC/Yong GC:指发生新生代的垃圾收集动作,Minor GC会非常的频繁,回收速度一般比较快;
Major GC/Full GC:一般会回收老年代,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC慢

垃圾回收算法
-
标记复制
将内存分为大小相同的两块,每次使用其中的一块。当一块用完后,将存活的对象复制到另一块去,然后把使用的空间一次清理掉。

-
标记清除
算法分为标记和清除阶段:标记存活的对象,统一回收所有未被标记的对象;也可以标记需要回收的对象,在标记完成后统一回收被标记的对象。它是最基础的手机算法,比较简单,但是会带来1个问题:清除后产生大量不连续的碎片

-
标记整理
根据老年代特有的标记算法,标记过程和标记-清除算法一样,但后续步骤不是直接对可回收的对象回收,而是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。

垃圾回收器
垃圾回收算法是内存分配的方法论,那么垃圾回收器是内存分配的具体实现

Serial收集器
Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。
大家看名字就知道这个收集器是一个单线程收集器了。它的“单线程”的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程(“StopTheWorld”),直到它收集结束。新生代采用复制算法,老年代采用标记-整理算法。
Serial Old收集器是Serial收集器的老年代版本,它同样是一个单线程收集器。可以和CMS收集器搭配使用

配置参数如下:
-XX:+UseSerialGC
-XX:+UseSerialOldGC
Parallel Scavenge收集器
Parallel收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,
其余行为(控制参数、收集算法、回收策略等等)和Serial收集器类似。
默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)
CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)
新生代采用复制算法,老年代采用标记-整理算法。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本。
使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑
ParallelScavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器)。

参数配置:
-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)
ParNew收集器
ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。
新生代采用复制算法,老年代采用标记-整理算法。

参数配置:
-XX:+UseParNewGC
CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
它非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。从名字中的MarkSweep这两个词可以看出,CMS收集器是一种“标记-清除”算法实现的,它的运作过程相比于前面
几种垃圾收集器来说更加复杂一些。
整个过程分为四个步骤:
初始标记:
暂停所有的其他线程(STW),并记录下gcroots直接能引用的对象,速度很快。
并发标记:
并发标记阶段就是从GCRoots的直接关联对象开始遍历整个对象图的过程,
这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
重新标记:
重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法做重新标记。
并发清理:开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理
并发重置:重置本次GC过程中的标记数据。

参数配置:
-XX:+UseConcMarkSweepGC(old)
Java虚拟机对对象的管理讲解的这里,有问题可以留言喔
相关文章:
一文了解Hotspot虚拟机下JAVA对象从创建到回收的生命周期
Java虚拟机是Java的核心和基础,他是Java编译器和操作系统平台之间处理器,能实现跨平台运行Java程序。本文主要讲解的是虚拟机如何管理对象,即Java对象在JVM虚拟机中被创建到回收的流程 Java对象从创建到回收的生命周期对象创建流程1.类加载检…...
【Java基础】Java对象创建的几种方式
先上关键内容,所用到的代码请参考文末示例代码。一、使用new关键字创建对象这是一种最常用的创建对象的方式。Student student1 new Student();二、使用Class的newInstance()方法创建对象需要有一个无参构造方法,这个newInstance()方法调用无参的构造函…...
社保缴费满15年就可以不缴了?6个很多人最关心的问题权威解答来了
一、社保缴费满15年就可以不缴了? 上海市政府新闻办公室2022年在微信号发文表示,社会保险是由国家通过立法强制建立的社会保障制度,用人单位和劳动者都必须依法参加社会保险。即使职工与用人单位商议签订了不参加社保的所谓“协议”…...
关于HDFS
目录 一、HDFS概述 二、HDFS架构与工作机制 三、HDFS的Shell操作 四、Hdfs的API操作 一、HDFS概述 HDFS:Hadoop Distributed File System;一种分布式文件管理系统,通过目录树定位文件。使用场景:一次写入,多次读出…...
C++入门:类 对象
C 在 C 语言的基础上增加了面向对象编程,C 支持面向对象程序设计。类是 C 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成…...
Python生日系统
#免费源码见文末公众号# 录入生日 def write():keyvar1.get()valuevar2.get()with open(d:\\生日系统.pickle,rb) as file:dictspickle.load(file)dicts[key]valuewith open(d:\\生日系统.pickle,wb) as file:pickle.dump(dicts,file)file.close() 查询生日 def read():namev…...
< CSDN周赛解析:第 28 期 >
CSDN周赛解析:第 27 期👉 第一题: 小Q的鲜榨柠檬汁> 题目解析> 解决方案👉 第二题: 三而竭> 解析> 解决方案> 拓展知识👉 第三题: 隧道逃生> 解析> 解决方案👉…...
【题外话】如何拯救小米11Pro这款工业垃圾
1 背景媳妇用小米11Pro手机,某日不慎摔落,幸好屏幕未碎,然而WiFi却怎样都无法打开,初以为是系统死机,几天依旧故障无法使用。现在的手机没有WiFi功能,就无法刷抖音、看视频,就是鸡肋了。后抽空去…...
Python中有哪些常用操作?这20个你都会吗
Python 是一个解释型语言,可读性与易用性让它越来越热门。 正如 Python 之禅中所述: 优美胜于丑陋,明了胜于晦涩。 在你的日常编码中,以下技巧可以给你带来意想不到的收获。 1、字符串反转 下面的代码片段,使用 P…...
【LeetCode】剑指 Offer(4)
目录 写在前面: 题目:剑指 Offer 10- I. 斐波那契数列 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 题目:剑指 Offer 10- II. …...
庄懂的TA笔记(十二)<>
庄懂的TA笔记(十二)<>一、作业展示,答疑:1、作业:2、答疑:二、作业示范,分析:1、文档分析:2、资源分析:3、资源优化:4、光…...
学分绩点(2023寒假每日一题 5)
北京大学对本科生的成绩施行平均学分绩点制(GPA)。 既将学生的实际考分根据不同的学科的不同学分按一定的公式进行计算。 公式如下: 实际成绩 绩点 90——100 4.0 85——89 3.7 82——84 3.3 78——81 3.0 75…...
Framework学习之旅:Zygote进程
概述 在Android系统中,DVM(Dalvik 虚拟机和ART、应用程序进程以及运行系统的关键服务SystemServer进程都是由Zygote进程来创建的。通过fork(复制进程)的形式来创建应用程进程和SystemServer进程,由于Zygote进程在启动时会创建DVM…...
HTTP基础知识
关键字:一问一答用于和服务器交互什么是HTTPHTTP是个应用层协议,是HTTP客户端和HTTP服务器之间的交互数据格式。所以这里有个实例:在浏览网页的时候,浏览器会向服务器发送一个HTTP请求,告诉服务器我想访问什么..然后服…...
Delphi 10.4.2使用传统代码提示方案(auto complete)(转)
Delphi 10.4重点是实现了LSP,但现在最新的10.4.2还是不成熟,无法满足日常需要,不过没关系,可以设置为原有的方案,如下图:具体操作:Tools->Options->Editor->language->Code Insight…...
存储类别、链接与内存管理(三)
1、malloc函数详解 (1)函数声明 #include <stdlib.h> void* malloc(size_t size);malloc可以申请一定数量的空闲内存,这样的内存是匿名的,也就是malloc不会为其赋名,但是确实返回动态分配内存块的首元素地址&a…...
Java:Linux(CentOS)安装、配置及相关命令
目录一、VMware安装二、CentOS安装1、安装过程2、加载ISO2.1 桌面的设置三、VI/VIM编辑器1、一般模式2、编辑模式3、命令模式4、模式间转换四、网络配置和系统管理操作1、配置子网IP和网关2、配置虚拟机ip地址2.1 ifconfig 查询ip地址2.2 修改IP地址3、配置主机名3.1 hostname …...
Linux 操作系统原理 — 多任务优先级调度策略
目录 文章目录 目录多任务优先级调度策略User Process 调度策略配置调整 User Process 的优先级调整非实时进程的优先级调整实时进程优先级调整 User Process 的调度算法多任务优先级调度策略 在 Linux Kernel 中,Kernel Thread 作为唯一的调度实体,Kernel Scheduler(调度程…...
链表学习之找到两个链表相交的第一个节点
链表解题技巧 额外的数据结构(哈希表);快慢指针;虚拟头节点; 找到两个链表相交的第一个节点 给定两个链表,这两个链表可能有环,可能无环。判断这两个链表是否相交,相交则返回第一…...
【Kubernetes】【十一】Pod详解 Pod的生命周期
Pod生命周期 我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程: pod创建过程 运行初始化容器(init container)过程 运行主容器(main container) 容器启动后钩子&#…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...
