学习笔记5——对象、直接内存、执行引擎,string

学习笔记系列开头惯例发布一些寻亲消息

链接:https://baobeihuijia.com/bbhj/contents/3/192486.html

创建对象的步骤

  • 对象对应的类是否被加载,链接(链接到真实的内存地址),初始化(类初始化)

  • 计算对象占用大小,在堆中划分内存

    • 内存规整:指针碰撞法(指针作为分界线向后移动)
    • 内存不规整:空闲列表分配(记录哪些内存是可用的)
    • 取决于java堆采用什么垃圾回收机制内存是否规整
  • 处理并发安全问题

    • 区域加锁
    • 线程预留TLAB
  • 初始化分配的空间:所有属性赋默认初始化值

  • 设置对象头

  • 对象初始化<显式初始化、代码块中初始化、构造器初始化>

对象在堆中的布局

  • 对象头

    • 运行时元数据

      • 在堆中的首地址(哈希值)
      • GC分代年龄
      • 锁状态
    • 类型指针:指向元空间的类型

  • 实例数据:各种类型的字段【先放父类的变量、相同宽度的分配在一起】

  • 对齐填充

对象访问定位

  • 句柄访问

    • 浪费空间
    • 访问效率较低
    • 优点:栈空间引用地址比较稳定,就算对象发生了变化,也只需要修改句柄指针,栈中的引用无需变化
  • 直接指针

直接内存(元数据)

  • 读写文件,需要与磁盘交互,需要由用户态切换到内核态
    • 一般的需要经过两层
  • 除了第一种,对于一些读写比较频繁的场合,java虚拟机提供了NIO库,使得用户程序可以操作一块由操作系统划出的直接内存,不受JVM的限制和管理,元数据就是这样实现的

执行引擎

  • 需要了解编译和汇编过程

    • 机器指令码:cpu直接识别执行,不同cpu对同一机器指令码的识别不同,可能对A是相加,对B就是赋值

    • 指令:指令就是将一些常用的机器指令码用符号的形式记录,比如一串二进制可以表示为add

    • 指令集:由于cpu之间对于机器指令识别的差异,所以对于不同种类cpu,都要有对应的(指令,二进制码)表,当然这是提前定义在cpu内部的

    • 汇编语言:助记符代替操作码,地址符号代替操作数的地址

    • 高级语言:高级语言一张嘴,机器指令跑断腿

  • 执行引擎的任务就是将字节码指令解释为平台上的本地机器指令,JVM执行引擎提供了三种方式

    • AOT编译器:是一种由源代码直接到机器码的方法,但是对于java 的动态特性并不友好,所以是一种牺牲质量换取性能的策略,如动态类加载无法实现
    • 解释器(字节码->机器码)
      • 将字节码翻译为机器码,可以很快启动
    • JIT编译器(java的即时编译技术just in time,相比于AOT,JIT是在程序运行过程中进行转换)(分为两类)
      • 寻找热点代码,对于这些频繁被调用的热点代码进行深度优化,然后将字节码编译为机器码后再启动,编译需要花费点时间,如果优化失败则还是采用翻译器
      • 热点代码探测:这里的热点代码指的是一段时间内被调用频率很高的代码,而不是从执行开始单纯的次数最多的代码(有衰减机制)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • JIT编译器:将字节码转化为本地机器码(启动慢过程很快)

    • c1编译模式:进行简单、可靠的优化耗时短,编译快,如有必要将加入性能监控的逻辑
    • c2编译模式:会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。
  • 为什么java需要编译器和翻译器合并?

String Table

  • 为什么要移动到堆中:想跟随堆中的其他对象一起进行垃圾回收,避免字符串常量越来越多导致内存溢出(原来常量池位于永久代,永久代只有full gc才会清理比较慢,移出来是为了让字符串常量池得到更加频繁的清理)
  • 拼接
    • 常量拼接直接字节码编译优化生成结果,存在常量池("javaee"),常量池中没有重复的元素,可以通过不同的变量名来索引相同的地址
    • 只要带一个变量,则拼接原理为stringbuilder对象append,结果放入堆中 / 如果是final string拼接那么还是按照常量拼接来
      • 如果没有显式操作,还是使用+,那么就是每次+都会创建builder对象和string对象,以stringbuilder的方式拼接
      • 显式调用stringbuilder.append后,只会调用一个builder的append,而且不会和常量池发生关系
    • 拼接结果调用intern方法,如果该字符串不存在就会将当前字符串放入常量池,返回结果的指向必须为常量池中的字符串实例,该方法确保字符串在内存中只有一份拷贝(intern就是让当前对象的值在池中也存一份,如果已经有了那就算了,还是指着对象,没有的话就存一份指向池中)
  • 池中的string pool是一个固定大小的hashtable,不会存储相同内容的字符串的
  • 显式的+两个双引号,或者tostring之后就会存储到常量池中,如果是对象或者存在一个变量的的话,采用的是stringbuilder进行的,所以不会存到常量池
  • StringBuffer的toString方法会调用new String, 在堆中新建对象

String的intern()方法在不同JDK版本的区别-CSDN博客

java 面试题: new String() 会创建几个对象?_new string("a") + new string("b") 会创建几个对象?-CSDN博客

相关推荐
袖清暮雨6 分钟前
Python刷题笔记
笔记·python·算法
六bring个六39 分钟前
QT上位机笔记
开发语言·笔记·qt
熬夜造bug42 分钟前
LeetCode Hot100 刷题笔记(1)—— 哈希、双指针、滑动窗口
笔记·leetcode·hot100
行思理2 小时前
go语言应该如何学习
开发语言·学习·golang
oceanweave3 小时前
【k8s学习之CSI】理解 LVM 存储概念和相关操作
学习·容器·kubernetes
花之亡灵4 小时前
.net6 中实现邮件发送
笔记·c#·.net·代码规范
LuoYaFu4 小时前
文件上传做题记录
笔记
吴梓穆4 小时前
UE5学习笔记 FPS游戏制作43 UI材质
笔记·学习·ue5
学会870上岸华师5 小时前
c语言学习16——内存函数
c语言·开发语言·学习
密码小丑5 小时前
玄机-apache日志分析
网络·笔记·apache