学习笔记系列开头惯例发布一些寻亲消息
链接: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博客