Java对象创建

1.类加载检查: 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到 这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那 必须先执行相应的类加载过程。
2.分配内存: 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类 加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分 配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是 否规整又由所采用的垃圾收集器是否带有压缩整理功能决定
内存分配的两种方式
选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是”标记-清除”,还是”标记-整理”(也称作”标记-压缩”),值得注意的是,复制算法内存也是规整的

3.初始化零值: 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象 头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这 些字段的数据类型所对应的零值。
4.设置对象头: 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实 例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对 象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
5.执行 init 方法: 在上面工作都完成之后,从虚拟机的视⻆来看,一个新的对象已经产生了,但从 Java 程序的视⻆来看,对象创建才刚开始,
对象的访问定位
建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有1.使用句柄和2.直接指针两种:
1.句柄: 如果使用句柄的话,那么Java堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息

2.直接指针: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型 数据的相关信息,而reference 中存储的直接就是对象的地址

这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地 址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针 访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。
判断对象是否存活
引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任 何时候计数器为0的对象就是不可能再被使用的。
可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索, 节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的

强引用,软引用,弱引用,虚引用
强引用
强引用是默认支持,当内存不足的时候,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会回收对象。
强引用是最常见的普通对象引用,只要还有强引用指向对象,对象就存活,垃圾回收器不会处理存活对象。一般把一个对象赋给一个引用变量,这个引用变量就是强引用。当一个对象被强引用变量所引用,它就处于可达状态,是不会被垃圾回收的,即使之后都不会再用到了,也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。
对于一个普通对象,如果没有其他引用关系,只要超过了引用的作用域或者显式地将相应的强引用赋值为null,一般认为就是可以被垃圾回收了。(具体的回收时机看垃圾回收策略)
下例中,b就是强引用。
public static void main(String[] args) {
Object a = new Object();
Object b = a;
a = null;
System.out.println(b);//java.lang.Object@4554617c
}
软引用
软引用是一种相对强引用弱化了一些的引用,用java.lang.ref.SoftReference实现,可以让对象豁免一些垃圾收集。当系统内存充足的时候,不会被回收;当系统内存不足的时候,会被回收。
软引用一般用于对内存敏感的程序中,比如高速缓存。
import java.lang.ref.SoftReference;
public class SoftReferenceDemo {
public static void main(String[] args) {
Object a = new Object();
SoftReference<Object> softReference = new SoftReference<>(a);//软引用
//a和软引用指向同一个对象
System.out.println(a);//java.lang.Object@4554617c
System.out.println(softReference.get());//java.lang.Object@4554617c 10
//内存够用,软引用不会被回收
a = null;
System.gc();//内存够用不会自动gc,手动唤醒gc
System.out.println(a);//null
System.out.println(softReference.get());//java.lang.Object@4554617c 16
//内存不够用时
try{ //配置Xms和Xmx为5MB
byte[] bytes = new byte[1024*1024*30];//设置30MB超内存
}catch (Throwable e){ e.printStackTrace(); }finally { System.out.println(a);//null
System.out.println(softReference.get());//null
}
}
}
弱引用
弱引用需要用java.lang.ref.WeakReference实现,它比软引用的生存期更短,对于弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否够,都会回收该对象的占用内存
import java.lang.ref.WeakReference;
public class SoftReferenceDemo {
public static void main(String[] args) {
Object a = new Object();
WeakReference<Object> softReference = new WeakReference<>(a);//软引用
//a和弱引用指向同一个对象
System.out.println(a);//java.lang.Object@4554617c
System.out.println(softReference.get());//java.lang.Object@4554617c 10
//内存够用,弱引用也会被回收
a = null; System.gc();//内存够用不会自动gc,手动唤醒gc
System.out.println(a);//null
System.out.println(softReference.get());//null
}
}
虚引用
虚引用要通过java.lang.ref.PhantomReference类来实现,虚引用不会决定对象的生命周期,如果一个对象只有虚引用,就相当于没有引用,在任何时候都可能会被垃圾回收器回收。它不能单独使用也不能访问对象,虚引用必须和引用队列联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被finalize以后,做某些事情的机制。
PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,设置虚引用关联唯一的目的是在对象被收集器回收的时候收到一个系统通知,或者后续添加进一步的处理。Java允许使用finalize()方法在垃圾回收器将对象从内存中清理出去之前做一些必要的清理工作。【例如实现一个监控对象的通知机制]
强引用:不回收。
软引用:内存不够就回收。
弱引用:一定回收。
虚引用:一定回收,get出来就是null,引用形同虚设,主要和引用队列联合使用,在finalize之前会被放到引用队列中。
对象头
https://www.pianshen.com/article/21361265803/

对象头(Object Header)
存储代表该对象运行时的一些信息, 这部分信息与对象体的内容没有关系.
Mark Word, 代表标记信息, 例如hash code, 锁的标志位, GC分代年龄等;
Class Word, 代表类型信息, 该部分是一个指针, 指向方法区(元数据空间) 中的实际类型.
由于在Mark Word中要存储不同的多种信息, 按照对象状态的不同, 又可以细分为5种状态的Mark Word.
- Normal – 普通状态, 由25位哈希码, 4位GC分代年龄, 1位偏向锁标识, 2位锁标志(01)组成.
- Biased – 偏向状态, 由23位线程标识, 2位时间戳, 4位GC分代年龄. 1位偏向锁标识, 2位锁标识(01)组成.
- Lightweight Locked – 轻量级锁状态, 由30位指针(指向锁记录), 2位锁标识(00)组成.
- Heavyweight Locked – 重量级锁状态, 由30位指针(指向重量级锁的
monitor, monitor是一个数据结构, 会存储其他抢锁的线程), 2位锁标识(10)组成. - Mared for GC – 待GC回收状态, 只有最后2位锁标识(11)有效.
###
文档信息
- 本文作者:Jessica
- 本文链接:https://jessica0530.github.io/2020/08/30/Java-%E5%AF%B9%E8%B1%A1/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)