java对象的创建与内存分配机制

news/2025/2/5 6:56:05/

文章目录

  • 对象的创建与内存分配机制
    • 对象的创建
      • 类加载检查
      • 分配内存
      • 初始化零值
      • 设置对象头
      • 指向init方法
      • 其他:指针压缩
    • 对象内存分配
      • 对象在栈上分配
      • 对象在Eden区中分配
      • 大对象直接分配到老年代
      • 长期存活的对象进入老年代
      • 对象动态年龄判断
      • 老年代空间分配担保机制
    • 对象的内存回收
      • 垃圾标记算法
      • 常见的引用类型
      • finalize()方法
      • 如何判断一个类是无用的类

对象的创建与内存分配机制

对象的创建

java中对象的创建过程如下图所示分为这几步:类加载检查、分配内存、初始化、设置对象头、执行init方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bL9W0Ebb-1678459769746)(picture/性能调优/123315)]



类加载检查

检查当前要创建对象所对应的类是否被加载过,类元数据信息是否已经在方法区中保存过,如果没有则会去进行类加载的过程,如果加载了则进行下一步



分配内存

这一部分需要考虑两点:

  • 应该将哪一块内存区域分配给对象
  • 如何解决多个线程并发分配同一块内存问题

如何分配内存

java中有两种为对象分配内存的方式:

  • 指针碰撞(Bump the Pointer) 默认使用

    对于绝对工整的内存区域,一边是已经分配的内存,一边是未分配的内存,中间有一个指针分割。

    经过内加载后就已经知道了此对象会占用多大的内存,从指针位置开始往后分配一块内存给该对象

  • 空闲列表(Free List)

    对于内存比较碎片化,部署绝对工整的情况,就需要使用空闲列表机制来分配内存了,JVM底层会维护一个空闲列表,然后选出一块合适该对象的内存区域进行分配

解决并发分配问题

在多线程的情况下,就可以会出现多个线程都再指针移动前读取到了当前指针的位置,然后把后面的一块内存区域进行了重复分配。解决这个问题有两种方法:

  • CAS算法

    再使用内存前先进行一次验证,比如判断指针是否移动了,如果验证不通过则进行重试

  • TLAB,JDK8默认使用

    本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)

    为每个线程在Eden区中事先分配一块内存区域,各个线程就在自己的这块区域中进行分配,这样就不会有并发问题,当这块内存区域不够使用时在使用CAS算法在Eden去中分配

    通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。



初始化零值

为对象的属性赋零值,这里需要注意,在类加载的时候是操作的静态变量,而这里才是操作的普通成员变量

初始化零值的作用是:避免了对象属性没有初始化就被使用



设置对象头

对象头的作用是:保存Hash值、分代年龄、锁信息、指向方法区中的类元信息等等

java的对象一般分为三部分:对象头、实例属性、对其填充

  • 对象头接下来详细讲
  • 实例属性:保存此对象各个属性的值
  • 对其填充:保证整个对象占用内存是8字节的整数倍

对象头如下图所示又分为三个部分

  • mark word标记阶段

    记录对象hash值、分代年龄、锁信息。32位机器占4字节,64为机器占8字节

  • Klass point 类型指针

    指向方法区中类元数据。64位机器开启指针压缩后占4字节,否则占8字节

    虚拟机通过这个指针来确定这个对象是哪个类的实例。

  • 数组长度:只有数组对象才有,占4字节

32位机器中的对象头信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QqDuJNwb-1678459769747)(picture/性能调优/123316)]

64位机器中的对象头信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i4ZEN4f0-1678459769747)(picture/性能调优/123317)]



指向init方法

对成员变量赋程序员自定义的值,并调用对象的构造方法



其他:指针压缩

JDK1.6开始,对于64为的机器默认是开启指针压缩的

启用指针压缩:-XX:+UseCompressedOops(默认开启),禁止指针压缩:-XX:-UseCompressedOops

之所以要使用指针压缩最主要的原因就是节约堆的内存使用



对象内存分配

一个对象的内存分配过程大致如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99q9JK41-1678459769747)(picture/性能调优/123318)]



对象在栈上分配

我们创建一个对象其实最开始的时候会判断该对象是否能够在栈上分配,目的是减轻GC的压力,当方法执行完 出栈后,这一块栈帧内存区域也就被回收了

经过逃逸分析,我们就能判断此对象是否能在栈上分配,其实就是看此对象有没有在该方法之外的地方被引用,是否有作为方法的返回值给其他地方引用。

同时还有标量替换机制,对象是需要一块连续的内存空间,而栈帧的内存可能没有这么大的一块连续的内存空间分配给对象,而标量替换就是将对象拆开,分散存储对象的成员属性。

标量与聚合量,java的基本类型就是标量,java的对象就是聚合量

逃逸分析和标量替换JDK1.7之后都是默认开启的,逃逸分析开关-XX:+DoEscapeAnalysis 标量替换开关-XX:+EliminateAllocations



对象在Eden区中分配

年轻代默认占用堆的1/3内存,其中Eden区大致占用8/10,S0占用1/10, S1占用1/10

JVM参数-XX:+UseAdaptiveSizePolicy(默认开启),会导致这个8:1:1比例自动变化,如果不想这个比例有变化可以设置参数-XX:-UseAdaptiveSizePolicy

一般情况下对象会在Eden区中创建,当存满后进行MinorGC,将存活的对象移至S0区,下一次MinorGC就回收Eden区和S0区,将存活对象移至S1,这其中分代年龄一直递增,默认达到15后就移至老年代

如果一个对象在经过MinorGC后,Survicor的剩余空间不足已存放该对象,这个对象就会提前 直接进入到老年代中

当老年代内存使用满后触发FullGC,回收堆和方法区的内存



大对象直接分配到老年代

JVM的参数 -XX:PretenureSizeThreshold能配置超过这个存储空间的对象就是大对象,直接分配到老年代中,目的是减少大对象来回拷贝占用Survicor的空间

这个参数只在 Serial 和ParNew两个收集器下有效。

所以我们一般都是两个参数一起设置JVM参数:-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC



长期存活的对象进入老年代

对象每经过一次GC,如果还存活该对象年龄则+1,默认达到15次后就进入老年代,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同

对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设



对象动态年龄判断

经过一次MinorGC后,存活的对象会存放在Survicor区,这一批对象总大小如果大于了当前Servicor区内存的一半,此时对象年龄大于等于这批对象最大年龄的对象就直接进入到老年代,比如此时Servicor区现有一些对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代

-XX:TargetSurvivorRatio 目标存活率,默认为50%

对象动态年龄判断机制一般是在minor gc之后触发的。



老年代空间分配担保机制

每经过一个MinorGC前都会计算当前老年代剩余可用空间,判断老年代剩余可用空间是否小于年轻代所有对象总大小

如果小于则检查当前是否有-XX:-HandlePromotionFailure参数,JDK8默认有

如果有这个参数则判断当前老年代剩余可用空间是否小于以往MinorGC移入老年代对象内存空间的平均值

如果小于或者上一步参数没有配置则直接进行FullGC,之后其实还是会进行一次MinorGC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c1BrSxtg-1678459769747)(picture/性能调优/123319)]



对象的内存回收

垃圾标记算法

  • 引用计数法,对象循环引用问题无法解决
  • 可达性分析算法,一般默认使用的这种算法,从GCRoot对象开始向下寻找引用。GFRoot对象是栈帧的成员变量、静态变量



常见的引用类型

  • 强引用

    普通的变量引用,GC不会被清理

  • 软引用

    将对象用SoftReference对象包裹,正常情况下不会被回收,如果经过一次GC后还是释放不出空间存放新的对象则会将软引用的对象回收

    public static SoftReference<User> user = new SoftReference<User>(new User());
    
  • 弱引用

    将对象用WeakReference软引用类型的对象包裹,弱引用跟没引用差不多,GC会直接回收掉,很少用

    public static WeakReference<User> user = new WeakReference<User>(new User());
    
  • 虚引用

    虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用



finalize()方法

finalize()方法最终判定对象是否存活,当一个对象被标记为垃圾对象后,如果该对象重写了finalize()方法则不会立刻回收该对象,此时该对象有一次自救的机会,只要重新与引用链上的任何的一个对象建立关联即可。那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

注意:一个对象的finalize()方法只会被执行一次,也就是说通过调用finalize方法自我救命的机会就一次。

finalize()方法的运行代价高昂, 不确定性大, 无法保证各个对象的调用顺序, 如今已被官方明确声明为不推荐使用的语法。



如何判断一个类是无用的类

在进行FullGC回收方法区中的内存时,就需要判断类是不是无用的类,

需要满足下面三个条件才是无用的类:

  • 类的实例对象都已经被回收了
  • 加载该类的类加载器被回收了
  • 堆中该类的java.lang.Class 对象没有在任何地方被引用

http://www.ppmy.cn/news/31625.html

相关文章

什么是UEFI签名认证?UEFI签名有什么好处?

为了防御恶意软件攻击&#xff0c;目前市面上所有电脑设备启动时默认开启安全启动(Secure Boot)模式。安全启动(Secure Boot)是UEFI扩展协议定义的安全标准&#xff0c;可以确保设备只使用OEM厂商信任的软件启动。UEFI签名认证就是对运行在 UEFI 系统下的 efi 驱动和通过 UEFI …

每天学一点之注解、元注解

注解 1、注解概述 定义&#xff1a; 注解&#xff08;Annotation&#xff09;&#xff0c;也叫元数据。与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面&#xff0c;用来对这些元素进行说明&#xff0c;注释。 作用分类&#…

Windows7,10使用:Vagrant+VirtualBox 安装 centos7

一、Vagrant&#xff0c;VirtualBox 是什么二、版本说明1、win7下建议安装版本2、win10下建议安装版本三、Windows7下安装1、安装Vagrant2、安装VirtualBox3、打开VirtualBox&#xff0c;配置虚拟机默认安装地址四、windows7下载.box文件&#xff0c;安装centos 71、下载一个.b…

Cobalt Strike---(2)

数据管理 Cobalt Strike 的团队服务器是行动期间Cobalt Strike 收集的所有信息的中间商。Cobalt Strike 解析来 自它的 Beacon payload 的输出&#xff0c;提取出目标、服务和凭据。 如果你想导出 Cobalt Strike 的数据&#xff0c;通过 Reporting → Export Data 。Cobalt Str…

【python】如何用python写一个下拉选择框和页签?

文章目录前言ttk模块下拉选择框combobox下拉选择框2页签Notebook前言 python学习之路任重而道远&#xff0c;要想学完说容易也容易&#xff0c;说难也难。 很多人说python最好学了&#xff0c;但扪心自问&#xff0c;你会用python做什么了&#xff1f; 刚开始在大学学习c语言&…

QML 模型(ListModel)

LIstModel&#xff08;列表模型&#xff09; ListModel 是ListElement定义的简单容器&#xff0c;每个定义都包含数据角色。内容可以在 QML 中动态定义或显式定义。 属性&#xff1a; count模型中数据条目的数量dynamic动态角色&#xff0c;默认情况下&#xff0c;角色的类型…

类和对象(一)

类和对象&#xff08;一&#xff09; C并不是纯面向对象语言 C是面向过程和面向对象语言的&#xff01; 面向过程和面向对象初步认识&#xff1a; C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 C是基…

[深入理解SSD系列 闪存实战2.1.3] 固态硬盘闪存的物理学原理_NAND Flash 的读、写、擦工作原理

2.1.3.1 Flash 的物理学原理与发明历程 经典物理学认为 物体越过势垒,有一阈值能量;粒子能量小于此能量则不能越过,大于此能 量则可以越过。例如骑自行车过小坡,先用力骑,如果坡很低,不蹬自行车也能 靠惯性过去。如果坡很高,不蹬自行车,车到一半就停住,然后退回去。 …